Implicitly initiated

- Pass partial tick to visualizers and Effect#visualize
- Pass partial tick to LitVisual#updateLight
- Remove Visual#init
- Rename LitVisual#initLightSectionNotifier to setLightSectionNotifier
- Add static utility methods to FlatLit
- Remove relight methods from AbstractVisual and add specialized relight methods to AbstractBlockEntityVisual and AbstractEntityVisual to match how vanilla retrieves lightmaps
- Rename AtomicBitset to AtomicBitSet
This commit is contained in:
PepperCode1 2024-07-01 13:16:27 -07:00
parent 6017228f4a
commit 06a2788f9c
29 changed files with 212 additions and 227 deletions

View file

@ -31,5 +31,6 @@ gradlePlugin {
}
dependencies {
implementation("dev.architectury.loom:dev.architectury.loom.gradle.plugin:1.6-SNAPSHOT")
// FIXME: This should not hard-code the Loom version.
implementation("dev.architectury.loom:dev.architectury.loom.gradle.plugin:1.6.397")
}

View file

@ -16,5 +16,5 @@ public interface Effect {
* @param ctx The visualization context.
* @return An arbitrary EffectVisual.
*/
List<EffectVisual<?>> visualize(VisualizationContext ctx);
List<EffectVisual<?>> visualize(VisualizationContext ctx, float partialTick);
}

View file

@ -13,16 +13,14 @@ import net.minecraft.core.SectionPos;
*/
public interface LitVisual extends Visual {
/**
* Called when a section this visual is contained in receives a light update.
* Set the notifier object.
*
* <p>Even if multiple sections are updated at the same time, this method will only be called once.</p>
* <p>This method is only called once right after the visual
* is created and before {@link #collectLightSections}.</p>
*
* <p>The implementation is free to parallelize calls to this method, as well as execute the plan
* returned by {@link DynamicVisual#planFrame} simultaneously. It is safe to query/update light here,
* but you must ensure proper synchronization if you want to mutate anything outside this visual or
* anything that is also mutated within {@link DynamicVisual#planFrame}.</p>
* @param notifier The notifier.
*/
void updateLight();
void setLightSectionNotifier(Notifier notifier);
/**
* Collect the sections that this visual is contained in.
@ -36,14 +34,18 @@ public interface LitVisual extends Visual {
void collectLightSections(LongConsumer consumer);
/**
* Set the notifier object.
* Called when a section this visual is contained in receives a light update.
*
* <p>This method is only called once, upon visual creation,
* after {@link #init} and before {@link #collectLightSections}.</p>
* <p>Even if multiple sections are updated at the same time, this method will only be called once.</p>
*
* @param notifier The notifier.
* <p>The implementation is free to parallelize calls to this method, as well as execute the plan
* returned by {@link DynamicVisual#planFrame} simultaneously. It is safe to query/update light here,
* but you must ensure proper synchronization if you want to mutate anything outside this visual or
* anything that is also mutated within {@link DynamicVisual#planFrame}.</p>
*
* <p>This method not is invoked automatically after visual creation.</p>
*/
void initLightSectionNotifier(Notifier notifier);
void updateLight(float partialTick);
/**
* A notifier object that can be used to indicate to the impl

View file

@ -8,13 +8,6 @@ package dev.engine_room.flywheel.api.visual;
* @see LitVisual
*/
public interface Visual {
/**
* Initialize instances here.
*
* <p>This method will be called exactly once upon visual creation.</p>
*/
void init(float partialTick);
/**
* Update instances here.
*

View file

@ -18,7 +18,7 @@ public interface BlockEntityVisualizer<T extends BlockEntity> {
* @param blockEntity The block entity to construct a visual for.
* @return The visual.
*/
List<BlockEntityVisual<? super T>> createVisual(VisualizationContext ctx, T blockEntity);
List<BlockEntityVisual<? super T>> createVisual(VisualizationContext ctx, T blockEntity, float partialTick);
/**
* Checks if the given block entity should not be rendered with the vanilla {@link BlockEntityRenderer}.

View file

@ -18,7 +18,7 @@ public interface EntityVisualizer<T extends Entity> {
* @param entity The entity to construct a visual for.
* @return The visual.
*/
List<EntityVisual<? super T>> createVisual(VisualizationContext ctx, T entity);
List<EntityVisual<? super T>> createVisual(VisualizationContext ctx, T entity, float partialTick);
/**
* Checks if the given entity should not render with the vanilla {@link EntityRenderer}.

View file

@ -8,7 +8,7 @@ import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.instance.InstanceType;
import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.backend.engine.embed.Environment;
import dev.engine_room.flywheel.lib.util.AtomicBitset;
import dev.engine_room.flywheel.lib.util.AtomicBitSet;
public abstract class AbstractInstancer<I extends Instance> implements Instancer<I> {
public final InstanceType<I> type;
@ -19,8 +19,8 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
protected final ArrayList<I> instances = new ArrayList<>();
protected final ArrayList<InstanceHandleImpl> handles = new ArrayList<>();
protected final AtomicBitset changed = new AtomicBitset();
protected final AtomicBitset deleted = new AtomicBitset();
protected final AtomicBitSet changed = new AtomicBitSet();
protected final AtomicBitSet deleted = new AtomicBitSet();
protected AbstractInstancer(InstanceType<I> type, Environment environment) {
this.type = type;

View file

@ -1,12 +1,18 @@
package dev.engine_room.flywheel.lib.instance;
import java.util.Iterator;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.lib.visual.AbstractVisual;
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
import dev.engine_room.flywheel.lib.visual.AbstractEntityVisual;
import net.minecraft.client.renderer.LightTexture;
/**
* An interface that implementors of {@link Instance} should also implement
* if they wish to make use of the relighting utilities in {@link AbstractVisual}.
* An interface that implementors of {@link Instance} should also implement if they wish to make use of
* {@link #relight} and the relighting utilities in {@link AbstractBlockEntityVisual} and {@link AbstractEntityVisual}.
*/
public interface FlatLit extends Instance {
/**
@ -25,4 +31,34 @@ public interface FlatLit extends Instance {
default FlatLit light(int blockLight, int skyLight) {
return light(LightTexture.pack(blockLight, skyLight));
}
static void relight(int packedLight, @Nullable FlatLit... instances) {
for (FlatLit instance : instances) {
if (instance != null) {
instance.light(packedLight)
.handle()
.setChanged();
}
}
}
static void relight(int packedLight, Iterator<@Nullable FlatLit> instances) {
while (instances.hasNext()) {
FlatLit instance = instances.next();
if (instance != null) {
instance.light(packedLight)
.handle()
.setChanged();
}
}
}
static void relight(int packedLight, Iterable<@Nullable FlatLit> instances) {
relight(packedLight, instances.iterator());
}
static void relight(int packedLight, Stream<@Nullable FlatLit> instances) {
relight(packedLight, instances.iterator());
}
}

View file

@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
// https://github.com/Netflix/hollow/blob/master/hollow/src/main/java/com/netflix/hollow/core/memory/ThreadSafeBitSet.java
// Refactored to remove unused methods, deduplicate some code segments, and add extra functionality with #forEachSetSpan
public class AtomicBitset {
public class AtomicBitSet {
// 1024 bits, 128 bytes, 16 longs per segment
public static final int DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS = 10;
@ -18,17 +18,17 @@ public class AtomicBitset {
private final int numLongsPerSegment;
private final int log2SegmentSize;
private final int segmentMask;
private final AtomicReference<AtomicBitsetSegments> segments;
private final AtomicReference<AtomicBitSetSegments> segments;
public AtomicBitset() {
public AtomicBitSet() {
this(DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS);
}
public AtomicBitset(int log2SegmentSizeInBits) {
public AtomicBitSet(int log2SegmentSizeInBits) {
this(log2SegmentSizeInBits, 0);
}
public AtomicBitset(int log2SegmentSizeInBits, int numBitsToPreallocate) {
public AtomicBitSet(int log2SegmentSizeInBits, int numBitsToPreallocate) {
if (log2SegmentSizeInBits < 6) {
throw new IllegalArgumentException("Cannot specify fewer than 64 bits in each segment!");
}
@ -40,7 +40,7 @@ public class AtomicBitset {
long numBitsPerSegment = numLongsPerSegment * 64L;
int numSegmentsToPreallocate = numBitsToPreallocate == 0 ? 1 : (int) (((numBitsToPreallocate - 1) / numBitsPerSegment) + 1);
segments = new AtomicReference<>(new AtomicBitsetSegments(numSegmentsToPreallocate, numLongsPerSegment));
segments = new AtomicReference<>(new AtomicBitSetSegments(numSegmentsToPreallocate, numLongsPerSegment));
}
public void set(int position) {
@ -251,7 +251,7 @@ public class AtomicBitset {
}
public long maxSetBit() {
AtomicBitsetSegments segments = this.segments.get();
AtomicBitSetSegments segments = this.segments.get();
int segmentIdx = segments.numSegments() - 1;
@ -273,7 +273,7 @@ public class AtomicBitset {
throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
}
AtomicBitsetSegments segments = this.segments.get();
AtomicBitSetSegments segments = this.segments.get();
int segmentPosition = segmentIndexForPosition(fromIndex);
if (segmentPosition >= segments.numSegments()) {
@ -309,7 +309,7 @@ public class AtomicBitset {
int segmentPosition = segmentIndexForPosition(fromIndex);
AtomicBitsetSegments segments = this.segments.get();
AtomicBitSetSegments segments = this.segments.get();
if (segmentPosition >= segments.numSegments()) {
return fromIndex;
@ -352,7 +352,7 @@ public class AtomicBitset {
* @param consumer The consumer to accept each span.
*/
public void forEachSetSpan(BitSpanConsumer consumer) {
AtomicBitsetSegments segments = this.segments.get();
AtomicBitSetSegments segments = this.segments.get();
if (segments.cardinality() == 0) {
return;
@ -415,7 +415,7 @@ public class AtomicBitset {
* Clear all bits to 0.
*/
public void clear() {
AtomicBitsetSegments segments = this.segments.get();
AtomicBitSetSegments segments = this.segments.get();
for (int i = 0; i < segments.numSegments(); i++) {
AtomicLongArray segment = segments.getSegment(i);
@ -478,13 +478,13 @@ public class AtomicBitset {
}
@NotNull
private AtomicBitsetSegments expandToFit(int segmentIndex) {
AtomicBitsetSegments visibleSegments = segments.get();
private AtomicBitSet.AtomicBitSetSegments expandToFit(int segmentIndex) {
AtomicBitSetSegments visibleSegments = segments.get();
while (visibleSegments.numSegments() <= segmentIndex) {
// Thread safety: newVisibleSegments contains all of the segments from the currently visible segments, plus extra.
// all of the segments in the currently visible segments are canonical and will not change.
AtomicBitsetSegments newVisibleSegments = new AtomicBitsetSegments(visibleSegments, segmentIndex + 1, numLongsPerSegment);
AtomicBitSetSegments newVisibleSegments = new AtomicBitSetSegments(visibleSegments, segmentIndex + 1, numLongsPerSegment);
// because we are using a compareAndSet, if this thread "wins the race" and successfully sets this variable, then the segments
// which are newly defined in newVisibleSegments become canonical.
@ -500,10 +500,10 @@ public class AtomicBitset {
return visibleSegments;
}
private static class AtomicBitsetSegments {
private static class AtomicBitSetSegments {
private final AtomicLongArray[] segments;
private AtomicBitsetSegments(int numSegments, int segmentLength) {
private AtomicBitSetSegments(int numSegments, int segmentLength) {
AtomicLongArray[] segments = new AtomicLongArray[numSegments];
for (int i = 0; i < numSegments; i++) {
@ -515,7 +515,7 @@ public class AtomicBitset {
this.segments = segments;
}
private AtomicBitsetSegments(AtomicBitsetSegments copyFrom, int numSegments, int segmentLength) {
private AtomicBitSetSegments(AtomicBitSetSegments copyFrom, int numSegments, int segmentLength) {
AtomicLongArray[] segments = new AtomicLongArray[numSegments];
for (int i = 0; i < numSegments; i++) {
@ -550,7 +550,7 @@ public class AtomicBitset {
@Override
public boolean equals(Object obj) {
if (!(obj instanceof AtomicBitset other)) {
if (!(obj instanceof AtomicBitSet other)) {
return false;
}
@ -558,8 +558,8 @@ public class AtomicBitset {
throw new IllegalArgumentException("Segment sizes must be the same");
}
AtomicBitsetSegments thisSegments = this.segments.get();
AtomicBitsetSegments otherSegments = other.segments.get();
AtomicBitSetSegments thisSegments = this.segments.get();
AtomicBitSetSegments otherSegments = other.segments.get();
for (int i = 0; i < thisSegments.numSegments(); i++) {
AtomicLongArray thisArray = thisSegments.getSegment(i);

View file

@ -11,7 +11,9 @@ import dev.engine_room.flywheel.api.visual.LitVisual;
import dev.engine_room.flywheel.api.visual.TickableVisual;
import dev.engine_room.flywheel.api.visualization.VisualManager;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.FlatLit;
import dev.engine_room.flywheel.lib.math.MoreMath;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -40,8 +42,8 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
@Nullable
protected LitVisual.Notifier notifier;
public AbstractBlockEntityVisual(VisualizationContext ctx, T blockEntity) {
super(ctx, blockEntity.getLevel());
public AbstractBlockEntityVisual(VisualizationContext ctx, T blockEntity, float partialTick) {
super(ctx, blockEntity.getLevel(), partialTick);
this.blockEntity = blockEntity;
this.pos = blockEntity.getBlockPos();
this.blockState = blockEntity.getBlockState();
@ -49,8 +51,8 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
}
@Override
public void init(float partialTick) {
updateLight();
public void setLightSectionNotifier(Notifier notifier) {
this.notifier = notifier;
}
@Override
@ -58,11 +60,6 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
consumer.accept(SectionPos.asLong(pos));
}
@Override
public void initLightSectionNotifier(Notifier notifier) {
this.notifier = notifier;
}
/**
* In order to accommodate for floating point precision errors at high coordinates,
* {@link VisualManager}s are allowed to arbitrarily adjust the origin, and
@ -100,4 +97,12 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
return !context.limiter()
.shouldUpdate(pos.distToCenterSqr(context.camera().getPosition()));
}
protected void relight(BlockPos pos, @Nullable FlatLit... instances) {
FlatLit.relight(LevelRenderer.getLightColor(level, pos), instances);
}
protected void relight(@Nullable FlatLit... instances) {
relight(pos, instances);
}
}

View file

@ -8,8 +8,12 @@ import dev.engine_room.flywheel.api.visual.EntityVisual;
import dev.engine_room.flywheel.api.visual.TickableVisual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
import dev.engine_room.flywheel.lib.instance.FlatLit;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.Vec3;
/**
@ -31,16 +35,12 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
protected final T entity;
protected final EntityVisibilityTester visibilityTester;
public AbstractEntityVisual(VisualizationContext ctx, T entity) {
super(ctx, entity.level());
public AbstractEntityVisual(VisualizationContext ctx, T entity, float partialTick) {
super(ctx, entity.level(), partialTick);
this.entity = entity;
visibilityTester = new EntityVisibilityTester(entity, ctx.renderOrigin(), 1.5f);
}
@Override
public void init(float partialTick) {
}
/**
* Calculate the distance squared between this visual and the given <em>level</em> position.
*
@ -84,4 +84,12 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
public boolean isVisible(FrustumIntersection frustum) {
return entity.noCulling || visibilityTester.check(frustum);
}
protected void relight(float partialTick, FlatLit... instances) {
BlockPos pos = BlockPos.containing(entity.getLightProbePosition(partialTick));
int blockLight = entity.isOnFire() ? 15 : level.getBrightness(LightLayer.BLOCK, pos);
int skyLight = level.getBrightness(LightLayer.SKY, pos);
int light = LightTexture.pack(blockLight, skyLight);
FlatLit.relight(light, instances);
}
}

View file

@ -1,18 +1,10 @@
package dev.engine_room.flywheel.lib.visual;
import java.util.Objects;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import dev.engine_room.flywheel.api.instance.InstancerProvider;
import dev.engine_room.flywheel.api.visual.Visual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.FlatLit;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LightLayer;
public abstract class AbstractVisual implements Visual {
/**
@ -27,7 +19,7 @@ public abstract class AbstractVisual implements Visual {
protected boolean deleted = false;
public AbstractVisual(VisualizationContext ctx, Level level) {
public AbstractVisual(VisualizationContext ctx, Level level, float partialTick) {
this.visualizationContext = ctx;
this.instancerProvider = ctx.instancerProvider();
this.renderOrigin = ctx.renderOrigin();
@ -49,46 +41,4 @@ public abstract class AbstractVisual implements Visual {
_delete();
deleted = true;
}
protected void relight(BlockPos pos, @Nullable FlatLit... instances) {
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), instances);
}
protected void relight(int block, int sky, @Nullable FlatLit... instances) {
for (FlatLit instance : instances) {
if (instance == null) {
continue;
}
instance.light(block, sky)
.handle()
.setChanged();
}
}
protected void relight(BlockPos pos, Stream<? extends @Nullable FlatLit> instances) {
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), instances);
}
protected void relight(int block, int sky, Stream<? extends @Nullable FlatLit> instances) {
instances.filter(Objects::nonNull)
.forEach(instance -> instance.light(block, sky)
.handle()
.setChanged());
}
protected void relight(BlockPos pos, Iterable<? extends @Nullable FlatLit> instances) {
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), instances);
}
protected void relight(int block, int sky, Iterable<? extends @Nullable FlatLit> instances) {
for (FlatLit instance : instances) {
if (instance == null) {
continue;
}
instance.light(block, sky)
.handle()
.setChanged();
}
}
}

View file

@ -24,8 +24,8 @@ public class SimpleBlockEntityVisualizer<T extends BlockEntity> implements Block
}
@Override
public List<BlockEntityVisual<? super T>> createVisual(VisualizationContext ctx, T blockEntity) {
return List.of(visualFactory.create(ctx, blockEntity));
public List<BlockEntityVisual<? super T>> createVisual(VisualizationContext ctx, T blockEntity, float partialTick) {
return List.of(visualFactory.create(ctx, blockEntity, partialTick));
}
@Override
@ -46,7 +46,7 @@ public class SimpleBlockEntityVisualizer<T extends BlockEntity> implements Block
@FunctionalInterface
public interface Factory<T extends BlockEntity> {
BlockEntityVisual<? super T> create(VisualizationContext ctx, T blockEntity);
BlockEntityVisual<? super T> create(VisualizationContext ctx, T blockEntity, float partialTick);
}
/**

View file

@ -9,8 +9,8 @@ import net.minecraft.world.entity.Entity;
public class SimpleEntityVisual<T extends Entity> extends AbstractEntityVisual<T> implements SimpleDynamicVisual {
protected final List<EntityComponent> components = new ArrayList<>();
public SimpleEntityVisual(VisualizationContext ctx, T entity) {
super(ctx, entity);
public SimpleEntityVisual(VisualizationContext ctx, T entity, float partialTick) {
super(ctx, entity, partialTick);
}
public void addComponent(EntityComponent component) {

View file

@ -24,8 +24,8 @@ public class SimpleEntityVisualizer<T extends Entity> implements EntityVisualize
}
@Override
public List<EntityVisual<? super T>> createVisual(VisualizationContext ctx, T entity) {
return List.of(visualFactory.create(ctx, entity));
public List<EntityVisual<? super T>> createVisual(VisualizationContext ctx, T entity, float partialTick) {
return List.of(visualFactory.create(ctx, entity, partialTick));
}
@Override
@ -46,7 +46,7 @@ public class SimpleEntityVisualizer<T extends Entity> implements EntityVisualize
@FunctionalInterface
public interface Factory<T extends Entity> {
EntityVisual<? super T> create(VisualizationContext ctx, T entity);
EntityVisual<? super T> create(VisualizationContext ctx, T entity, float partialTick);
}
/**

View file

@ -53,13 +53,13 @@ public class BlockEntityStorage extends Storage<BlockEntity> {
}
@Override
protected List<? extends BlockEntityVisual<?>> createRaw(BlockEntity obj) {
protected List<? extends BlockEntityVisual<?>> createRaw(BlockEntity obj, float partialTick) {
var visualizer = VisualizationHelper.getVisualizer(obj);
if (visualizer == null) {
return List.of();
}
var visualList = visualizer.createVisual(visualizationContextSupplier.get(), obj);
var visualList = visualizer.createVisual(visualizationContextSupplier.get(), obj, partialTick);
BlockPos blockPos = obj.getBlockPos();
posLookup.put(blockPos.asLong(), visualList);

View file

@ -14,8 +14,8 @@ public class EffectStorage extends Storage<Effect> {
}
@Override
protected List<? extends EffectVisual<?>> createRaw(Effect obj) {
return obj.visualize(visualizationContextSupplier.get());
protected List<? extends EffectVisual<?>> createRaw(Effect obj, float partialTick) {
return obj.visualize(visualizationContextSupplier.get(), partialTick);
}
@Override

View file

@ -16,13 +16,13 @@ public class EntityStorage extends Storage<Entity> {
}
@Override
protected List<? extends EntityVisual<?>> createRaw(Entity obj) {
protected List<? extends EntityVisual<?>> createRaw(Entity obj, float partialTick) {
var visualizer = VisualizationHelper.getVisualizer(obj);
if (visualizer == null) {
return List.of();
}
return visualizer.createVisual(visualizationContextSupplier.get(), obj);
return visualizer.createVisual(visualizationContextSupplier.get(), obj, partialTick);
}
@Override

View file

@ -51,11 +51,12 @@ public class LitVisualStorage {
});
long updateId = getNextUpdateId();
Updater.Context updaterContext = new Updater.Context(updateId, context.partialTick());
for (long section : sectionsUpdatedThisFrame) {
var visuals = sections2Visuals.get(section);
if (visuals != null && !visuals.isEmpty()) {
taskExecutor.execute(() -> Distribute.tasks(taskExecutor, updateId, sync, visuals, Updater::updateLight));
taskExecutor.execute(() -> Distribute.tasks(taskExecutor, updaterContext, sync, visuals, Updater::updateLight));
} else {
sync.decrementAndEventuallyRun();
}
@ -89,8 +90,8 @@ public class LitVisualStorage {
return visuals2Sections.isEmpty();
}
public void addAndInitNotifier(LitVisual visual) {
visual.initLightSectionNotifier(new LitVisualNotifierImpl(visual));
public void setNotifierAndAdd(LitVisual visual) {
visual.setLightSectionNotifier(new LitVisualNotifierImpl(visual));
add(visual);
}
@ -168,15 +169,15 @@ public class LitVisualStorage {
// Breaking this into 2 separate cases allows us to avoid the overhead of atomics in the common case.
sealed interface Updater {
void updateLight(long updateId);
void updateLight(Context ctx);
LitVisual visual();
// The visual is only in one section. In this case, we can just update the visual directly.
record Simple(LitVisual visual) implements Updater {
@Override
public void updateLight(long updateId) {
visual.updateLight();
public void updateLight(Context ctx) {
visual.updateLight(ctx.partialTick);
}
}
@ -184,14 +185,17 @@ public class LitVisualStorage {
// even when multiple sections it was contained in are updated at the same time.
record Synced(LitVisual visual, AtomicLong updateId) implements Updater {
@Override
public void updateLight(long updateId) {
public void updateLight(Context ctx) {
// Different update ID means we won, so we can update the visual.
// Same update ID means another thread beat us to the update.
if (this.updateId.getAndSet(updateId) != updateId) {
visual.updateLight();
if (this.updateId.getAndSet(ctx.updateId) != ctx.updateId) {
visual.updateLight(ctx.partialTick);
}
}
}
record Context(long updateId, float partialTick) {
}
}
private final class LitVisualNotifierImpl implements LitVisual.Notifier {

View file

@ -81,14 +81,14 @@ public abstract class Storage<T> {
visuals.replaceAll((obj, visuals) -> {
visuals.forEach(Visual::delete);
var out = createRaw(obj);
var out = createRaw(obj, partialTick);
if (out.isEmpty()) {
return null;
}
for (Visual visual : out) {
setup(visual, partialTick);
setup(visual);
}
return out;
@ -108,7 +108,7 @@ public abstract class Storage<T> {
}
private void create(T obj, float partialTick) {
var visuals = createRaw(obj);
var visuals = createRaw(obj, partialTick);
if (visuals.isEmpty()) {
return;
@ -116,11 +116,11 @@ public abstract class Storage<T> {
this.visuals.put(obj, visuals);
for (Visual visual : visuals) {
setup(visual, partialTick);
setup(visual);
}
}
protected abstract List<? extends Visual> createRaw(T obj);
protected abstract List<? extends Visual> createRaw(T obj, float partialTick);
public Plan<DynamicVisual.Context> framePlan() {
return NestedPlan.of(dynamicVisuals, litVisuals.plan(), ForEachPlan.of(() -> simpleDynamicVisuals, SimpleDynamicVisual::beginFrame));
@ -134,9 +134,7 @@ public abstract class Storage<T> {
litVisuals.enqueueLightUpdateSection(section);
}
private void setup(Visual visual, float partialTick) {
visual.init(partialTick);
private void setup(Visual visual) {
if (visual instanceof TickableVisual tickable) {
if (visual instanceof SimpleTickableVisual simpleTickable) {
simpleTickableVisuals.add(simpleTickable);
@ -154,7 +152,7 @@ public abstract class Storage<T> {
}
if (visual instanceof LitVisual lit) {
litVisuals.addAndInitNotifier(lit);
litVisuals.setNotifierAndAdd(lit);
}
}

View file

@ -26,24 +26,19 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
return new SingleMeshModel(ModelPartConverter.convert(ModelLayers.BELL, BellRenderer.BELL_RESOURCE_LOCATION.sprite(), "bell_body"), Materials.BELL);
});
private OrientedInstance bell;
private final OrientedInstance bell;
private boolean wasShaking = false;
public BellVisual(VisualizationContext ctx, BellBlockEntity blockEntity) {
super(ctx, blockEntity);
}
public BellVisual(VisualizationContext ctx, BellBlockEntity blockEntity, float partialTick) {
super(ctx, blockEntity, partialTick);
@Override
public void init(float partialTick) {
bell = createBellInstance().setPivot(0.5f, 0.75f, 0.5f)
.setPosition(getVisualPosition());
bell.setChanged();
updateRotation(partialTick);
super.init(partialTick);
updateLight(partialTick);
}
private OrientedInstance createBellInstance() {
@ -81,8 +76,8 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
}
@Override
public void updateLight() {
relight(pos, bell);
public void updateLight(float partialTick) {
relight(bell);
}
@Override

View file

@ -51,22 +51,20 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
return new SingleMeshModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "lock"), Materials.CHEST);
});
private OrientedInstance bottom;
private TransformedInstance lid;
private TransformedInstance lock;
private final OrientedInstance bottom;
private final TransformedInstance lid;
private final TransformedInstance lock;
private final ChestType chestType;
private final Float2FloatFunction lidProgress;
private ChestType chestType;
private final Quaternionf baseRotation = new Quaternionf();
private Float2FloatFunction lidProgress;
private float lastProgress = Float.NaN;
public ChestVisual(VisualizationContext ctx, T blockEntity) {
super(ctx, blockEntity);
}
public ChestVisual(VisualizationContext ctx, T blockEntity, float partialTick) {
super(ctx, blockEntity, partialTick);
@Override
public void init(float partialTick) {
chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
Material texture = Sheets.chooseMaterial(blockEntity, chestType, isChristmas());
@ -90,8 +88,7 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
bottom.setChanged();
applyLidTransform(lidProgress.get(partialTick));
super.init(partialTick);
updateLight(partialTick);
}
private OrientedInstance createBottomInstance(Material texture) {
@ -153,8 +150,8 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
}
@Override
public void updateLight() {
relight(pos, bottom, lid, lock);
public void updateLight(float partialTick) {
relight(bottom, lid, lock);
}
@Override

View file

@ -38,19 +38,32 @@ public class MinecartVisual<T extends AbstractMinecart> extends SimpleEntityVisu
public static final ModelHolder SPAWNER_BODY_MODEL = createBodyModelHolder(ModelLayers.SPAWNER_MINECART);
public static final ModelHolder TNT_BODY_MODEL = createBodyModelHolder(ModelLayers.TNT_MINECART);
private final ModelHolder bodyModel;
private TransformedInstance body;
private final TransformedInstance body;
@Nullable
private TransformedInstance contents;
private BlockState blockState;
private boolean active;
private final ModelHolder bodyModel;
private final PoseStack stack = new PoseStack();
public MinecartVisual(VisualizationContext ctx, T entity, ModelHolder bodyModel) {
super(ctx, entity);
private BlockState blockState;
private boolean active;
public MinecartVisual(VisualizationContext ctx, T entity, float partialTick, ModelHolder bodyModel) {
super(ctx, entity, partialTick);
this.bodyModel = bodyModel;
body = createBodyInstance();
blockState = entity.getDisplayBlockState();
contents = createContentsInstance();
addComponent(new ShadowComponent(visualizationContext, entity).radius(0.7f));
addComponent(new FireComponent(visualizationContext, entity));
addComponent(new HitboxComponent(visualizationContext, entity));
updateInstances(partialTick);
updateLight(partialTick);
}
private static ModelHolder createBodyModelHolder(ModelLayerLocation layer) {
@ -59,22 +72,6 @@ public class MinecartVisual<T extends AbstractMinecart> extends SimpleEntityVisu
});
}
@Override
public void init(float partialTick) {
addComponent(new ShadowComponent(visualizationContext, entity).radius(0.7f));
addComponent(new FireComponent(visualizationContext, entity));
addComponent(new HitboxComponent(visualizationContext, entity));
body = createBodyInstance();
blockState = entity.getDisplayBlockState();
contents = createContentsInstance();
updateInstances(partialTick);
updateLight();
super.init(partialTick);
}
private TransformedInstance createBodyInstance() {
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, bodyModel.get())
.createInstance();
@ -110,8 +107,6 @@ public class MinecartVisual<T extends AbstractMinecart> extends SimpleEntityVisu
}
contents = createContentsInstance();
}
updateLight();
}
@Override
@ -198,6 +193,9 @@ public class MinecartVisual<T extends AbstractMinecart> extends SimpleEntityVisu
stack.scale(-1.0F, -1.0F, 1.0F);
body.setTransform(stack)
.setChanged();
// TODO: Use LitVisual if possible.
updateLight(partialTick);
}
protected void updateContents(TransformedInstance contents, PoseStack stack, float partialTick) {
@ -205,8 +203,8 @@ public class MinecartVisual<T extends AbstractMinecart> extends SimpleEntityVisu
.setChanged();
}
public void updateLight() {
relight(entity.blockPosition(), body, contents);
public void updateLight(float partialTick) {
relight(partialTick, body, contents);
}
@Override

View file

@ -34,19 +34,16 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
return new SingleMeshModel(ModelPartConverter.convert(ModelLayers.SHULKER, texture.sprite(), "lid"), Materials.SHULKER);
});
private TransformedInstance base;
private TransformedInstance lid;
private final TransformedInstance base;
private final TransformedInstance lid;
private final PoseStack stack = new PoseStack();
private float lastProgress = Float.NaN;
public ShulkerBoxVisual(VisualizationContext ctx, ShulkerBoxBlockEntity blockEntity) {
super(ctx, blockEntity);
}
public ShulkerBoxVisual(VisualizationContext ctx, ShulkerBoxBlockEntity blockEntity, float partialTick) {
super(ctx, blockEntity, partialTick);
@Override
public void init(float partialTick) {
DyeColor color = blockEntity.getColor();
Material texture;
if (color == null) {
@ -67,12 +64,11 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
.translateY(-1);
base = createBaseInstance(texture).setTransform(stack);
lid = createLidInstance(texture).setTransform(stack);
base.setChanged();
lid = createLidInstance(texture).setTransform(stack);
lid.setChanged();
super.init(partialTick);
updateLight(partialTick);
}
private TransformedInstance createBaseInstance(Material texture) {
@ -119,8 +115,8 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
}
@Override
public void updateLight() {
relight(pos, base, lid);
public void updateLight(float partialTick) {
relight(base, lid);
}
@Override

View file

@ -11,8 +11,8 @@ import net.minecraft.world.entity.vehicle.MinecartTNT;
public class TntMinecartVisual<T extends MinecartTNT> extends MinecartVisual<T> {
private static final int WHITE_OVERLAY = OverlayTexture.pack(OverlayTexture.u(1.0F), 10);
public TntMinecartVisual(VisualizationContext ctx, T entity) {
super(ctx, entity, TNT_BODY_MODEL);
public TntMinecartVisual(VisualizationContext ctx, T entity, float partialTick) {
super(ctx, entity, partialTick, TNT_BODY_MODEL);
}
@Override

View file

@ -47,27 +47,27 @@ public class VanillaVisuals {
.apply();
builder(EntityType.CHEST_MINECART)
.factory((ctx, entity) -> new MinecartVisual<>(ctx, entity, MinecartVisual.CHEST_BODY_MODEL))
.factory((ctx, entity, partialTick) -> new MinecartVisual<>(ctx, entity, partialTick, MinecartVisual.CHEST_BODY_MODEL))
.skipVanillaRender(MinecartVisual::shouldSkipRender)
.apply();
builder(EntityType.COMMAND_BLOCK_MINECART)
.factory((ctx, entity) -> new MinecartVisual<>(ctx, entity, MinecartVisual.COMMAND_BLOCK_BODY_MODEL))
.factory((ctx, entity, partialTick) -> new MinecartVisual<>(ctx, entity, partialTick, MinecartVisual.COMMAND_BLOCK_BODY_MODEL))
.skipVanillaRender(MinecartVisual::shouldSkipRender)
.apply();
builder(EntityType.FURNACE_MINECART)
.factory((ctx, entity) -> new MinecartVisual<>(ctx, entity, MinecartVisual.FURNACE_BODY_MODEL))
.factory((ctx, entity, partialTick) -> new MinecartVisual<>(ctx, entity, partialTick, MinecartVisual.FURNACE_BODY_MODEL))
.skipVanillaRender(MinecartVisual::shouldSkipRender)
.apply();
builder(EntityType.HOPPER_MINECART)
.factory((ctx, entity) -> new MinecartVisual<>(ctx, entity, MinecartVisual.HOPPER_BODY_MODEL))
.factory((ctx, entity, partialTick) -> new MinecartVisual<>(ctx, entity, partialTick, MinecartVisual.HOPPER_BODY_MODEL))
.skipVanillaRender(MinecartVisual::shouldSkipRender)
.apply();
builder(EntityType.MINECART)
.factory((ctx, entity) -> new MinecartVisual<>(ctx, entity, MinecartVisual.STANDARD_BODY_MODEL))
.factory((ctx, entity, partialTick) -> new MinecartVisual<>(ctx, entity, partialTick, MinecartVisual.STANDARD_BODY_MODEL))
.skipVanillaRender(MinecartVisual::shouldSkipRender)
.apply();
builder(EntityType.SPAWNER_MINECART)
.factory((ctx, entity) -> new MinecartVisual<>(ctx, entity, MinecartVisual.SPAWNER_BODY_MODEL))
.factory((ctx, entity, partialTick) -> new MinecartVisual<>(ctx, entity, partialTick, MinecartVisual.SPAWNER_BODY_MODEL))
.skipVanillaRender(MinecartVisual::shouldSkipRender)
.apply();
builder(EntityType.TNT_MINECART)

View file

@ -7,9 +7,9 @@ public class TestAtomicBitset {
@Test
void testNextClearBit() {
var segmentLength = 1 << AtomicBitset.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS;
var bitLength = 2 << AtomicBitset.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS;
var bs = new AtomicBitset(AtomicBitset.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS, bitLength);
var segmentLength = 1 << AtomicBitSet.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS;
var bitLength = 2 << AtomicBitSet.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS;
var bs = new AtomicBitSet(AtomicBitSet.DEFAULT_LOG2_SEGMENT_SIZE_IN_BITS, bitLength);
Assertions.assertEquals(0, bs.nextClearBit(0));
Assertions.assertEquals(1, bs.nextClearBit(1));

View file

@ -19,7 +19,8 @@ forge_version_range = [47.0.0,)
# General build dependency versions
java_version = 17
arch_loom_version = 1.6-SNAPSHOT
# FIXME: This doesn't do anything.
# arch_loom_version = 1.6-SNAPSHOT
cursegradle_version = 1.4.0
parchment_version = 2023.09.03

View file

@ -12,10 +12,11 @@ pluginManagement {
maven("https://maven.parchmentmc.org")
}
plugins {
val arch_loom_version: String by settings
id("dev.architectury.loom") version arch_loom_version
}
// FIXME: This doesn't do anything. The actual version is always the one defined in buildSrc/build.gradle.kts.
// plugins {
// val arch_loom_version: String by settings
// id("dev.architectury.loom") version arch_loom_version
// }
}
rootProject.name = "Flywheel"