Skip to content

Commit

Permalink
Implicitly initiated
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
PepperCode1 committed Jul 1, 2024
1 parent 6017228 commit 06a2788
Show file tree
Hide file tree
Showing 29 changed files with 209 additions and 224 deletions.
3 changes: 2 additions & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@
*/
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.
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@
* @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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
/**
Expand All @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

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

Expand All @@ -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!");
}
Expand All @@ -40,7 +40,7 @@ public AtomicBitset(int log2SegmentSizeInBits, int numBitsToPreallocate) {
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) {
Expand Down Expand Up @@ -251,7 +251,7 @@ public boolean get(int position) {
}

public long maxSetBit() {
AtomicBitsetSegments segments = this.segments.get();
AtomicBitSetSegments segments = this.segments.get();

int segmentIdx = segments.numSegments() - 1;

Expand All @@ -273,7 +273,7 @@ public int nextSetBit(int fromIndex) {
throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
}

AtomicBitsetSegments segments = this.segments.get();
AtomicBitSetSegments segments = this.segments.get();

int segmentPosition = segmentIndexForPosition(fromIndex);
if (segmentPosition >= segments.numSegments()) {
Expand Down Expand Up @@ -309,7 +309,7 @@ public int nextClearBit(int fromIndex) {

int segmentPosition = segmentIndexForPosition(fromIndex);

AtomicBitsetSegments segments = this.segments.get();
AtomicBitSetSegments segments = this.segments.get();

if (segmentPosition >= segments.numSegments()) {
return fromIndex;
Expand Down Expand Up @@ -352,7 +352,7 @@ public int cardinality() {
* @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;
Expand Down Expand Up @@ -415,7 +415,7 @@ public boolean isEmpty() {
* 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);
Expand Down Expand Up @@ -478,13 +478,13 @@ private AtomicLongArray segmentForPosition(int segmentIndex) {
}

@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.
Expand All @@ -500,10 +500,10 @@ private AtomicBitsetSegments expandToFit(int segmentIndex) {
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++) {
Expand All @@ -515,7 +515,7 @@ private AtomicBitsetSegments(int numSegments, int segmentLength) {
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++) {
Expand Down Expand Up @@ -550,16 +550,16 @@ public AtomicLongArray getSegment(int index) {

@Override
public boolean equals(Object obj) {
if (!(obj instanceof AtomicBitset other)) {
if (!(obj instanceof AtomicBitSet other)) {
return false;
}

if (other.log2SegmentSize != log2SegmentSize) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
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;
Expand Down Expand Up @@ -40,29 +42,24 @@ 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();
this.visualPos = pos.subtract(renderOrigin);
}

@Override
public void init(float partialTick) {
updateLight();
public void setLightSectionNotifier(Notifier notifier) {
this.notifier = notifier;
}

@Override
public void collectLightSections(LongConsumer consumer) {
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
Expand Down Expand Up @@ -100,4 +97,12 @@ public boolean doDistanceLimitThisFrame(DynamicVisual.Context context) {
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);
}
}
Loading

0 comments on commit 06a2788

Please sign in to comment.