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
registrate_version = MC1.20-1.3.3
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_version = 15.10.0.39
curios_minecraft_version = 1.20.1

View file

@ -5,17 +5,16 @@ import java.util.function.Consumer;
import com.mojang.math.Axis;
import com.simibubi.create.content.kinetics.base.KineticBlockEntity;
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.Instancer;
import dev.engine_room.flywheel.api.visual.DynamicVisual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
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.visual.SimpleDynamicVisual;
import dev.engine_room.flywheel.lib.visual.util.SmartRecycler;
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.bytes.ByteList;
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 {
private final OrientedInstance coil;
private final SelectInstance<OrientedInstance> magnet;
private final GroupInstance<OrientedInstance> rope;
private final ConditionalInstance<OrientedInstance> halfRope;
private final TransformedInstance magnet;
private final SmartRecycler<Boolean, TransformedInstance> rope;
protected final Direction rotatingAbout;
protected final Axis rotationAxis;
@ -50,14 +48,12 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
.position(getVisualPosition());
coil.setChanged();
magnet = new SelectInstance<>(this::getMagnetModelIndex);
magnet.addModel(getMagnetModel())
.addModel(getHalfMagnetModel());
magnet = magnetInstancer().createInstance();
rope = new GroupInstance<>(getRopeModel());
halfRope = new ConditionalInstance<>(getHalfRopeModel()).withCondition(this::shouldRenderHalfRope);
rope = new SmartRecycler<>(b -> b ? getHalfRopeModel().createInstance() : getRopeModel().createInstance());
updateOffset(partialTick);
updateLight(partialTick);
}
@Override
@ -66,67 +62,68 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
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> getHalfRopeModel();
protected abstract Instancer<TransformedInstance> getHalfRopeModel();
protected abstract float getOffset(float pt);
protected abstract boolean isRunning();
private Instancer<TransformedInstance> magnetInstancer() {
return offset > .25f ? getMagnetModel() : getHalfMagnetModel();
}
@Override
public void beginFrame(DynamicVisual.Context ctx) {
updateOffset(ctx.partialTick());
coil.rotation(rotationAxis.rotationDegrees(offset * 180))
.setChanged();
int neededRopeCount = getNeededRopeCount();
rope.resize(neededRopeCount);
magnet.setVisible(isRunning() || offset == 0);
magnet.update()
.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();
});
magnetInstancer().stealInstance(magnet);
halfRope.update()
.get()
.ifPresent(rope1 -> {
float f = offset % 1;
float halfRopeNudge = f > .75f ? f - 1 : f;
magnet.setIdentityTransform()
.translate(getVisualPosition())
.translate(0, -offset, 0)
.light(lightCache.getPackedLight(Math.max(0, Mth.floor(offset))))
.setChanged();
int light = lightCache.getPackedLight(0);
rope1.position(getVisualPosition())
.translatePosition(0, -halfRopeNudge, 0)
.light(light)
.setChanged();
});
rope.resetCount();
if (shouldRenderHalfRope()) {
float f = offset % 1;
float halfRopeNudge = f > .75f ? f - 1 : f;
rope.get(true).setIdentityTransform()
.translate(getVisualPosition())
.translate(0, -halfRopeNudge, 0)
.light(lightCache.getPackedLight(0))
.setChanged();
}
if (isRunning()) {
int size = rope.size();
for (int i = 0; i < size; i++) {
int light = lightCache.getPackedLight(size - 1 - i);
int neededRopeCount = getNeededRopeCount();
rope.get(i)
.position(getVisualPosition())
.translatePosition(0, -offset + i + 1, 0)
.light(light)
for (int i = 0; i < neededRopeCount; i++) {
rope.get(false)
.setIdentityTransform()
.translate(getVisualPosition())
.translate(0, -offset + i + 1, 0)
.light(lightCache.getPackedLight(neededRopeCount - 1 - i))
.setChanged();
}
} else {
rope.clear();
}
rope.discardExtra();
}
@Override
@ -151,21 +148,11 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
return offset > .75f && (f < .25f || f > .75f);
}
private int getMagnetModelIndex() {
if (isRunning() || offset == 0) {
return offset > .25f ? 0 : 1;
} else {
return -1;
}
}
@Override
public void collectCrumblingInstances(Consumer<Instance> consumer) {
super.collectCrumblingInstances(consumer);
consumer.accept(coil);
magnet.forEach(consumer);
rope.forEach(consumer);
halfRope.forEach(consumer);
consumer.accept(magnet);
}
@Override
@ -173,8 +160,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
super._delete();
coil.delete();
magnet.delete();
rope.clear();
halfRope.delete();
rope.delete();
}
private class LightCache {
@ -186,6 +172,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
public void setSize(int size) {
if (size != data.size()) {
data.size(size);
update();
int sectionCount = MoreMath.ceilingDiv(size + 15 - pos.getY() + pos.getY() / 4 * 4, SectionPos.SECTION_SIZE);
if (sectionCount != this.sectionCount) {
@ -215,7 +202,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
for (int i = 0; i < data.size(); i++) {
int blockLight = level.getBrightness(LightLayer.BLOCK, 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);
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.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.OrientedInstance;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.model.Models;
public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity> {
@ -15,18 +16,18 @@ public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity
}
@Override
protected Instancer<OrientedInstance> getRopeModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE));
protected Instancer<TransformedInstance> getRopeModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE));
}
@Override
protected Instancer<OrientedInstance> getMagnetModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_MAGNET));
protected Instancer<TransformedInstance> getMagnetModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_MAGNET));
}
@Override
protected Instancer<OrientedInstance> getHalfMagnetModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_HALF_MAGNET));
protected Instancer<TransformedInstance> getHalfMagnetModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_HALF_MAGNET));
}
@Override
@ -35,8 +36,8 @@ public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity
}
@Override
protected Instancer<OrientedInstance> getHalfRopeModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_HALF));
protected Instancer<TransformedInstance> getHalfRopeModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_HALF));
}
@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.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.OrientedInstance;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.model.Models;
public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> {
@ -17,18 +18,18 @@ public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> {
}
@Override
protected Instancer<OrientedInstance> getRopeModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, VirtualRenderHelper.blockModel(AllBlocks.ROPE.getDefaultState()));
protected Instancer<TransformedInstance> getRopeModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(AllBlocks.ROPE.getDefaultState()));
}
@Override
protected Instancer<OrientedInstance> getMagnetModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, VirtualRenderHelper.blockModel(AllBlocks.PULLEY_MAGNET.getDefaultState()));
protected Instancer<TransformedInstance> getMagnetModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(AllBlocks.PULLEY_MAGNET.getDefaultState()));
}
@Override
protected Instancer<OrientedInstance> getHalfMagnetModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.ROPE_HALF_MAGNET));
protected Instancer<TransformedInstance> getHalfMagnetModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.ROPE_HALF_MAGNET));
}
@Override
@ -37,8 +38,8 @@ public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> {
}
@Override
protected Instancer<OrientedInstance> getHalfRopeModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.ROPE_HALF));
protected Instancer<TransformedInstance> getHalfRopeModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.ROPE_HALF));
}
@Override

View file

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