mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-06 04:16:36 +01:00
Context for Tickable/Dynamic visuals
- Add VisualFrameContext for DynamicVisual#beginFrame - Add VisualTickContext for TickableVisual#tick - Move checks for update limiting to within the update calls themselves - Provide update limiting/culling primitives within (B)E Visuals - Remove methods from *Visual interfaces related to update limiting - Add thenMap and andMap to Plan - Add Plan primitive to transform context - Used in Visual update dispatch
This commit is contained in:
parent
e64976df5d
commit
d783617a73
27 changed files with 231 additions and 230 deletions
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.api.task;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface Plan<C> {
|
||||
/**
|
||||
* Submit this plan for execution.
|
||||
|
@ -31,6 +33,16 @@ public interface Plan<C> {
|
|||
*/
|
||||
Plan<C> then(Plan<C> plan);
|
||||
|
||||
/**
|
||||
* Create a new plan that executes this plan, then transforms the context to execute the given plan.
|
||||
*
|
||||
* @param map A function that transforms the plan context.
|
||||
* @param plan The plan to execute after this plan with the transformed context.
|
||||
* @param <D> The type of the transformed context.
|
||||
* @return The composed plan.
|
||||
*/
|
||||
<D> Plan<C> thenMap(Function<C, D> map, Plan<D> plan);
|
||||
|
||||
/**
|
||||
* Create a new plan that executes this plan and the given plan in parallel.
|
||||
*
|
||||
|
@ -39,6 +51,16 @@ public interface Plan<C> {
|
|||
*/
|
||||
Plan<C> and(Plan<C> plan);
|
||||
|
||||
/**
|
||||
* Create a new plan that executes this plan and the given plan in parallel.
|
||||
*
|
||||
* @param map A function that transforms the plan context.
|
||||
* @param plan The plan to execute in parallel with this plan than accepts the transformed context.
|
||||
* @param <D> The type of the transformed context.
|
||||
* @return The composed plan.
|
||||
*/
|
||||
<D> Plan<C> andMap(Function<C, D> map, Plan<D> plan);
|
||||
|
||||
/**
|
||||
* If possible, create a new plan that accomplishes everything
|
||||
* this plan does but with a simpler execution schedule.
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
package com.jozufozu.flywheel.impl.visualization.ratelimit;
|
||||
package com.jozufozu.flywheel.api.visual;
|
||||
|
||||
/**
|
||||
* Interface for rate-limiting updates based on an object's distance from the camera.
|
||||
*/
|
||||
public interface DistanceUpdateLimiter {
|
||||
/**
|
||||
* Call this before every update.
|
||||
*/
|
||||
void tick();
|
||||
|
||||
/**
|
||||
* Check to see if an object at the given position relative to the camera should be updated.
|
||||
*
|
||||
* @param distanceSquared
|
||||
* @param distanceSquared The distance squared from the camera to the object.
|
||||
* @return {@code true} if the object should be updated, {@code false} otherwise.
|
||||
*/
|
||||
boolean shouldUpdate(double distanceSquared);
|
|
@ -1,7 +1,5 @@
|
|||
package com.jozufozu.flywheel.api.visual;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||
|
||||
|
@ -10,39 +8,17 @@ import com.jozufozu.flywheel.api.instance.Instancer;
|
|||
* the start of a frame. By implementing {@link DynamicVisual}, an {@link Visual}
|
||||
* can animate its models in ways that could not be easily achieved by shader attribute
|
||||
* parameterization.
|
||||
*
|
||||
* <br><br> If your goal is offloading work to shaders, but you're unsure exactly how you need
|
||||
* <p>
|
||||
* If your goal is offloading work to shaders, but you're unsure exactly how you need
|
||||
* to parameterize the instances, you're encouraged to implement this for prototyping.
|
||||
*/
|
||||
public interface DynamicVisual extends Visual {
|
||||
/**
|
||||
* Called every frame, and after initialization.
|
||||
* <br>
|
||||
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside this visual.
|
||||
* <br>
|
||||
* Called every frame.
|
||||
* <p>
|
||||
* <b>DISPATCHED IN PARALLEL</b>. Ensure proper synchronization if you need to mutate anything outside this visual.
|
||||
* <p>
|
||||
* {@link Instancer}/{@link Instance} creation/acquisition is safe here.
|
||||
*/
|
||||
void beginFrame();
|
||||
|
||||
/**
|
||||
* As a further optimization, dynamic visuals that are far away are updated less often.
|
||||
* This behavior can be disabled by returning false.
|
||||
*
|
||||
* <br> You might want to opt out of this if you want your animations to remain smooth
|
||||
* even when far away from the camera. It is recommended to keep this as is, however.
|
||||
*
|
||||
* @return {@code true} if your visual should be slow updated.
|
||||
*/
|
||||
default boolean decreaseFramerateWithDistance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check this visual against a frustum.<p>
|
||||
* An implementor may choose to return a constant to skip the frustum check.
|
||||
*
|
||||
* @param frustum A frustum intersection tester for the current frame.
|
||||
* @return {@code true} if this visual should be considered for updates.
|
||||
*/
|
||||
boolean isVisible(FrustumIntersection frustum);
|
||||
void beginFrame(VisualFrameContext context);
|
||||
}
|
||||
|
|
|
@ -21,23 +21,11 @@ import com.jozufozu.flywheel.api.instance.Instancer;
|
|||
*/
|
||||
public interface TickableVisual extends Visual {
|
||||
/**
|
||||
* Called every tick, and after initialization.<p>
|
||||
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this visual
|
||||
* without proper synchronization.<p>
|
||||
* Called every tick.
|
||||
* <p>
|
||||
* <b>DISPATCHED IN PARALLEL</b>. Ensure proper synchronization if you need to mutate anything outside this visual.
|
||||
* <p>
|
||||
* {@link Instancer}/{@link Instance} creation/acquisition is safe here.
|
||||
*/
|
||||
void tick();
|
||||
|
||||
/**
|
||||
* As a further optimization, tickable visuals that are far away are ticked less often.
|
||||
* This behavior can be disabled by returning false.
|
||||
*
|
||||
* <br> You might want to opt out of this if you want your animations to remain smooth
|
||||
* even when far away from the camera. It is recommended to keep this as is, however.
|
||||
*
|
||||
* @return {@code true} if your visual should be slow ticked.
|
||||
*/
|
||||
default boolean decreaseTickRateWithDistance() {
|
||||
return true;
|
||||
}
|
||||
void tick(VisualTickContext c);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ package com.jozufozu.flywheel.api.visual;
|
|||
|
||||
/**
|
||||
* A general interface providing information about any type of thing that could use Flywheel's visualized rendering.
|
||||
*
|
||||
* @see DynamicVisual
|
||||
* @see TickableVisual
|
||||
*/
|
||||
public interface Visual {
|
||||
/**
|
||||
|
@ -30,16 +33,6 @@ public interface Visual {
|
|||
*/
|
||||
boolean shouldReset();
|
||||
|
||||
/**
|
||||
* Calculate the distance squared between this visual and the given <em>world</em> position.
|
||||
*
|
||||
* @param x The x coordinate.
|
||||
* @param y The y coordinate.
|
||||
* @param z The z coordinate.
|
||||
* @return The distance squared between this visual and the given position.
|
||||
*/
|
||||
double distanceSquared(double x, double y, double z);
|
||||
|
||||
/**
|
||||
* Free any acquired resources.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.jozufozu.flywheel.api.visual;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
|
||||
public record VisualFrameContext(double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum,
|
||||
DistanceUpdateLimiter limiter) {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.jozufozu.flywheel.api.visual;
|
||||
|
||||
public record VisualTickContext(double cameraX, double cameraY, double cameraZ, DistanceUpdateLimiter limiter) {
|
||||
}
|
|
@ -12,7 +12,7 @@ import com.jozufozu.flywheel.api.material.Material;
|
|||
import com.jozufozu.flywheel.api.material.MaterialVertexTransformer;
|
||||
import com.jozufozu.flywheel.api.task.Plan;
|
||||
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||
import com.jozufozu.flywheel.lib.task.RunOnAllWithContextPlan;
|
||||
import com.jozufozu.flywheel.lib.task.RunOnAllPlan;
|
||||
import com.jozufozu.flywheel.lib.vertex.VertexTransformations;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Matrix3f;
|
||||
|
@ -38,7 +38,7 @@ public class TransformCall<I extends Instance> {
|
|||
meshVertexCount = mesh.getVertexCount();
|
||||
boundingSphere = mesh.mesh.getBoundingSphere();
|
||||
|
||||
drawPlan = RunOnAllWithContextPlan.of(instancer::getAll, (instance, ctx) -> {
|
||||
drawPlan = RunOnAllPlan.of(instancer::getAll, (instance, ctx) -> {
|
||||
var boundingSphere = new Vector4f(this.boundingSphere);
|
||||
|
||||
boundingSphereTransformer.transform(boundingSphere, instance);
|
||||
|
|
|
@ -82,7 +82,7 @@ public class VisualWorld implements AutoCloseable {
|
|||
/**
|
||||
* Tick the visuals after the game has ticked:
|
||||
* <p>
|
||||
* Call {@link TickableVisual#tick()} on all visuals in this world.
|
||||
* Call {@link TickableVisual#tick} on all visuals in this world.
|
||||
* </p>
|
||||
*/
|
||||
public void tick(double cameraX, double cameraY, double cameraZ) {
|
||||
|
@ -96,7 +96,7 @@ public class VisualWorld implements AutoCloseable {
|
|||
* <p>
|
||||
* Check and update the render origin.
|
||||
* <br>
|
||||
* Call {@link DynamicVisual#beginFrame()} on all visuals in this world.
|
||||
* Call {@link DynamicVisual#beginFrame} on all visuals in this world.
|
||||
* </p>
|
||||
*/
|
||||
public void beginFrame(RenderContext context) {
|
||||
|
|
|
@ -6,23 +6,25 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
|||
import com.jozufozu.flywheel.api.task.Plan;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visual.VisualTickContext;
|
||||
import com.jozufozu.flywheel.config.FlwConfig;
|
||||
import com.jozufozu.flywheel.impl.TickContext;
|
||||
import com.jozufozu.flywheel.impl.visualization.FrameContext;
|
||||
import com.jozufozu.flywheel.impl.visualization.ratelimit.BandedPrimeLimiter;
|
||||
import com.jozufozu.flywheel.impl.visualization.ratelimit.DistanceUpdateLimiter;
|
||||
import com.jozufozu.flywheel.impl.visualization.ratelimit.DistanceUpdateLimiterImpl;
|
||||
import com.jozufozu.flywheel.impl.visualization.ratelimit.NonLimiter;
|
||||
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||
import com.jozufozu.flywheel.impl.visualization.storage.Transaction;
|
||||
import com.jozufozu.flywheel.lib.task.RunOnAllWithContextPlan;
|
||||
import com.jozufozu.flywheel.lib.task.RunOnAllPlan;
|
||||
import com.jozufozu.flywheel.lib.task.SimplePlan;
|
||||
import com.jozufozu.flywheel.util.Unit;
|
||||
|
||||
public abstract class VisualManager<T> {
|
||||
private final Queue<Transaction<T>> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
protected DistanceUpdateLimiter tickLimiter;
|
||||
protected DistanceUpdateLimiter frameLimiter;
|
||||
protected DistanceUpdateLimiterImpl tickLimiter;
|
||||
protected DistanceUpdateLimiterImpl frameLimiter;
|
||||
|
||||
public VisualManager() {
|
||||
tickLimiter = createUpdateLimiter();
|
||||
|
@ -31,8 +33,9 @@ public abstract class VisualManager<T> {
|
|||
|
||||
protected abstract Storage<T> getStorage();
|
||||
|
||||
protected DistanceUpdateLimiter createUpdateLimiter() {
|
||||
if (FlwConfig.get().limitUpdates()) {
|
||||
protected DistanceUpdateLimiterImpl createUpdateLimiter() {
|
||||
if (FlwConfig.get()
|
||||
.limitUpdates()) {
|
||||
return new BandedPrimeLimiter();
|
||||
} else {
|
||||
return new NonLimiter();
|
||||
|
@ -90,13 +93,7 @@ public abstract class VisualManager<T> {
|
|||
tickLimiter.tick();
|
||||
processQueue();
|
||||
})
|
||||
.then(RunOnAllWithContextPlan.of(getStorage()::getTickableVisuals, this::tickInstance));
|
||||
}
|
||||
|
||||
protected void tickInstance(TickableVisual instance, TickContext c) {
|
||||
if (!instance.decreaseTickRateWithDistance() || tickLimiter.shouldUpdate(instance.distanceSquared(c.cameraX(), c.cameraY(), c.cameraZ()))) {
|
||||
instance.tick();
|
||||
}
|
||||
.thenMap(this::createVisualTickContext, RunOnAllPlan.of(getStorage()::getTickableVisuals, TickableVisual::tick));
|
||||
}
|
||||
|
||||
public Plan<FrameContext> createFramePlan() {
|
||||
|
@ -104,14 +101,14 @@ public abstract class VisualManager<T> {
|
|||
frameLimiter.tick();
|
||||
processQueue();
|
||||
})
|
||||
.then(RunOnAllWithContextPlan.of(getStorage()::getDynamicVisuals, this::updateInstance));
|
||||
.thenMap(this::createVisualContext, RunOnAllPlan.of(getStorage()::getDynamicVisuals, DynamicVisual::beginFrame));
|
||||
}
|
||||
|
||||
protected void updateInstance(DynamicVisual instance, FrameContext c) {
|
||||
if (!instance.decreaseFramerateWithDistance() || frameLimiter.shouldUpdate(instance.distanceSquared(c.cameraX(), c.cameraY(), c.cameraZ()))) {
|
||||
if (instance.isVisible(c.frustum())) {
|
||||
instance.beginFrame();
|
||||
}
|
||||
}
|
||||
private VisualFrameContext createVisualContext(FrameContext ctx) {
|
||||
return new VisualFrameContext(ctx.cameraX(), ctx.cameraY(), ctx.cameraZ(), ctx.frustum(), frameLimiter);
|
||||
}
|
||||
|
||||
private VisualTickContext createVisualTickContext(TickContext ctx) {
|
||||
return new VisualTickContext(ctx.cameraX(), ctx.cameraY(), ctx.cameraZ(), frameLimiter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package com.jozufozu.flywheel.impl.visualization.ratelimit;
|
|||
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
public class BandedPrimeLimiter implements DistanceUpdateLimiter {
|
||||
public class BandedPrimeLimiter implements DistanceUpdateLimiterImpl {
|
||||
// 1 followed by the prime numbers
|
||||
private static final int[] DIVISOR_SEQUENCE = new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 };
|
||||
private static final int[] DIVISOR_SEQUENCE = new int[]{1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
|
||||
|
||||
private int tickCount = 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.jozufozu.flywheel.impl.visualization.ratelimit;
|
||||
|
||||
import com.jozufozu.flywheel.api.visual.DistanceUpdateLimiter;
|
||||
|
||||
public interface DistanceUpdateLimiterImpl extends DistanceUpdateLimiter {
|
||||
/**
|
||||
* Call this before every update.
|
||||
*/
|
||||
void tick();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.jozufozu.flywheel.impl.visualization.ratelimit;
|
||||
|
||||
public class NonLimiter implements DistanceUpdateLimiter {
|
||||
public class NonLimiter implements DistanceUpdateLimiterImpl {
|
||||
@Override
|
||||
public void tick() {
|
||||
}
|
||||
|
|
|
@ -32,12 +32,10 @@ public abstract class AbstractStorage<T> implements Storage<T> {
|
|||
|
||||
if (visual instanceof TickableVisual tickable) {
|
||||
tickableVisuals.add(tickable);
|
||||
tickable.tick();
|
||||
}
|
||||
|
||||
if (visual instanceof DynamicVisual dynamic) {
|
||||
dynamicVisuals.add(dynamic);
|
||||
dynamic.beginFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package com.jozufozu.flywheel.lib.task;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.jozufozu.flywheel.api.task.Plan;
|
||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||
|
||||
public record MapContextPlan<C, D>(Function<C, D> map, Plan<D> plan) implements SimplyComposedPlan<C> {
|
||||
@Override
|
||||
public void execute(TaskExecutor taskExecutor, C context, Runnable onCompletion) {
|
||||
D newContext = map.apply(context);
|
||||
plan.execute(taskExecutor, newContext, onCompletion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan<C> maybeSimplify() {
|
||||
var maybeSimplified = plan.maybeSimplify();
|
||||
|
||||
if (maybeSimplified instanceof UnitPlan) {
|
||||
return UnitPlan.of();
|
||||
}
|
||||
|
||||
return new MapContextPlan<>(map, maybeSimplified);
|
||||
}
|
||||
}
|
|
@ -1,35 +1,36 @@
|
|||
package com.jozufozu.flywheel.lib.task;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.jozufozu.flywheel.api.task.Plan;
|
||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
public record RunOnAllPlan<T>(Supplier<List<T>> listSupplier, Consumer<T> action) implements ContextAgnosticPlan {
|
||||
public static <T, C> Plan<C> of(Supplier<List<T>> iterable, Consumer<T> forEach) {
|
||||
return new RunOnAllPlan<>(iterable, forEach).cast();
|
||||
public record RunOnAllPlan<T, C>(Supplier<List<T>> listSupplier,
|
||||
BiConsumer<T, C> action) implements SimplyComposedPlan<C> {
|
||||
public static <T, C> Plan<C> of(Supplier<List<T>> iterable, BiConsumer<T, C> forEach) {
|
||||
return new RunOnAllPlan<>(iterable, forEach);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(TaskExecutor taskExecutor, Runnable onCompletion) {
|
||||
public void execute(TaskExecutor taskExecutor, C context, Runnable onCompletion) {
|
||||
taskExecutor.execute(() -> {
|
||||
var list = listSupplier.get();
|
||||
final int size = list.size();
|
||||
|
||||
if (size == 0) {
|
||||
onCompletion.run();
|
||||
} else if (size <= getChunkingThreshold()) {
|
||||
processList(list, onCompletion);
|
||||
} else if (size <= getChunkSize(taskExecutor, size)) {
|
||||
processList(list, context, onCompletion);
|
||||
} else {
|
||||
dispatchChunks(list, taskExecutor, onCompletion);
|
||||
dispatchChunks(list, taskExecutor, context, onCompletion);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchChunks(List<T> suppliedList, TaskExecutor taskExecutor, Runnable onCompletion) {
|
||||
private void dispatchChunks(List<T> suppliedList, TaskExecutor taskExecutor, C context, Runnable onCompletion) {
|
||||
final int size = suppliedList.size();
|
||||
final int chunkSize = getChunkSize(taskExecutor, size);
|
||||
|
||||
|
@ -42,7 +43,7 @@ public record RunOnAllPlan<T>(Supplier<List<T>> listSupplier, Consumer<T> action
|
|||
int start = Math.max(remaining, 0);
|
||||
|
||||
var subList = suppliedList.subList(start, end);
|
||||
taskExecutor.execute(() -> processList(subList, synchronizer));
|
||||
taskExecutor.execute(() -> processList(subList, context, synchronizer));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,14 +51,10 @@ public record RunOnAllPlan<T>(Supplier<List<T>> listSupplier, Consumer<T> action
|
|||
return MoreMath.ceilingDiv(totalSize, taskExecutor.getThreadCount() * 32);
|
||||
}
|
||||
|
||||
private void processList(List<T> suppliedList, Runnable onCompletion) {
|
||||
private void processList(List<T> suppliedList, C context, Runnable onCompletion) {
|
||||
for (T t : suppliedList) {
|
||||
action.accept(t);
|
||||
action.accept(t, context);
|
||||
}
|
||||
onCompletion.run();
|
||||
}
|
||||
|
||||
private static int getChunkingThreshold() {
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
package com.jozufozu.flywheel.lib.task;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.jozufozu.flywheel.api.task.Plan;
|
||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||
|
||||
public record RunOnAllWithContextPlan<T, C>(Supplier<List<T>> listSupplier,
|
||||
BiConsumer<T, C> action) implements SimplyComposedPlan<C> {
|
||||
public static <T, C> Plan<C> of(Supplier<List<T>> iterable, BiConsumer<T, C> forEach) {
|
||||
return new RunOnAllWithContextPlan<>(iterable, forEach);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(TaskExecutor taskExecutor, C context, Runnable onCompletion) {
|
||||
taskExecutor.execute(() -> {
|
||||
var list = listSupplier.get();
|
||||
final int size = list.size();
|
||||
|
||||
if (size == 0) {
|
||||
onCompletion.run();
|
||||
} else if (size <= getChunkingThreshold()) {
|
||||
processList(list, context, onCompletion);
|
||||
} else {
|
||||
dispatchChunks(list, taskExecutor, context, onCompletion);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchChunks(List<T> suppliedList, TaskExecutor taskExecutor, C context, Runnable onCompletion) {
|
||||
final int size = suppliedList.size();
|
||||
final int chunkSize = getChunkSize(taskExecutor, size);
|
||||
|
||||
var synchronizer = new Synchronizer(MoreMath.ceilingDiv(size, chunkSize), onCompletion);
|
||||
int remaining = size;
|
||||
|
||||
while (remaining > 0) {
|
||||
int end = remaining;
|
||||
remaining -= chunkSize;
|
||||
int start = Math.max(remaining, 0);
|
||||
|
||||
var subList = suppliedList.subList(start, end);
|
||||
taskExecutor.execute(() -> processList(subList, context, synchronizer));
|
||||
}
|
||||
}
|
||||
|
||||
private static int getChunkSize(TaskExecutor taskExecutor, int totalSize) {
|
||||
return MoreMath.ceilingDiv(totalSize, taskExecutor.getThreadCount() * 32);
|
||||
}
|
||||
|
||||
private void processList(List<T> suppliedList, C context, Runnable onCompletion) {
|
||||
for (T t : suppliedList) {
|
||||
action.accept(t, context);
|
||||
}
|
||||
onCompletion.run();
|
||||
}
|
||||
|
||||
private static int getChunkingThreshold() {
|
||||
return 256;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.lib.task;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.jozufozu.flywheel.api.task.Plan;
|
||||
|
||||
public interface SimplyComposedPlan<C> extends Plan<C> {
|
||||
|
@ -8,11 +10,21 @@ public interface SimplyComposedPlan<C> extends Plan<C> {
|
|||
return new BarrierPlan<>(this, plan);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <D> Plan<C> thenMap(Function<C, D> map, Plan<D> plan) {
|
||||
return then(new MapContextPlan<>(map, plan));
|
||||
}
|
||||
|
||||
@Override
|
||||
default Plan<C> and(Plan<C> plan) {
|
||||
return NestedPlan.of(this, plan);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <D> Plan<C> andMap(Function<C, D> map, Plan<D> plan) {
|
||||
return and(new MapContextPlan<>(map, plan));
|
||||
}
|
||||
|
||||
@Override
|
||||
default Plan<C> maybeSimplify() {
|
||||
return this;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.lib.task;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.jozufozu.flywheel.api.task.Plan;
|
||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||
|
||||
|
@ -24,11 +26,21 @@ public class UnitPlan<C> implements Plan<C> {
|
|||
return plan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Plan<C> thenMap(Function<C, D> map, Plan<D> plan) {
|
||||
return new MapContextPlan<>(map, plan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan<C> and(Plan<C> plan) {
|
||||
return plan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Plan<C> andMap(Function<C, D> map, Plan<D> plan) {
|
||||
return new MapContextPlan<>(map, plan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan<C> maybeSimplify() {
|
||||
return this;
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.joml.FrustumIntersection;
|
|||
import com.jozufozu.flywheel.api.visual.BlockEntityVisual;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager;
|
||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
||||
|
@ -50,11 +51,6 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
|
|||
return blockEntity.getBlockState() != blockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distanceSquared(double x, double y, double z) {
|
||||
return pos.distToCenterSqr(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBox getVolume() {
|
||||
return MutableBox.from(pos);
|
||||
|
@ -72,8 +68,24 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
|
|||
return visualPos;
|
||||
}
|
||||
|
||||
public boolean isVisible(FrustumIntersection frustum) {
|
||||
return frustum.testAab(visualPos.getX(), visualPos.getY(), visualPos.getZ(),
|
||||
visualPos.getX() + 1, visualPos.getY() + 1, visualPos.getZ() + 1);
|
||||
/**
|
||||
* @param frustum The current frustum.
|
||||
* @return {@code true} if this visual within the given frustum.
|
||||
*/
|
||||
public boolean visible(FrustumIntersection frustum) {
|
||||
return frustum.testAab(visualPos.getX(), visualPos.getY(), visualPos.getZ(), visualPos.getX() + 1, visualPos.getY() + 1, visualPos.getZ() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits which frames this visual is updated on based on its distance from the camera.
|
||||
* <p>
|
||||
* You may optionally do this check to avoid updating your visual every frame when it is far away.
|
||||
*
|
||||
* @param context The current frame context.
|
||||
* @return {@code true} if this visual shouldn't be updated this frame based on its distance from the camera.
|
||||
*/
|
||||
public boolean doDistanceLimitThisFrame(VisualFrameContext context) {
|
||||
return !context.limiter()
|
||||
.shouldUpdate(pos.distToCenterSqr(context.cameraX(), context.cameraY(), context.cameraZ()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,16 +35,23 @@ import net.minecraft.world.phys.Vec3;
|
|||
public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVisual implements EntityVisual<T>, TickingLightListener {
|
||||
protected final T entity;
|
||||
protected final MutableBox bounds;
|
||||
protected final EntityVisibilityTester boxTracker;
|
||||
protected final EntityVisibilityTester visibilityTester;
|
||||
|
||||
public AbstractEntityVisual(VisualizationContext ctx, T entity) {
|
||||
super(ctx, entity.level);
|
||||
this.entity = entity;
|
||||
bounds = MutableBox.from(entity.getBoundingBox());
|
||||
boxTracker = new EntityVisibilityTester(entity, ctx.renderOrigin());
|
||||
visibilityTester = new EntityVisibilityTester(entity, ctx.renderOrigin());
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Calculate the distance squared between this visual and the given <em>world</em> position.
|
||||
*
|
||||
* @param x The x coordinate.
|
||||
* @param y The y coordinate.
|
||||
* @param z The z coordinate.
|
||||
* @return The distance squared between this visual and the given position.
|
||||
*/
|
||||
public double distanceSquared(double x, double y, double z) {
|
||||
return entity.distanceToSqr(x, y, z);
|
||||
}
|
||||
|
@ -92,15 +99,10 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
|
|||
*/
|
||||
public Vector3f getVisualPosition(float partialTicks) {
|
||||
Vec3 pos = entity.position();
|
||||
return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()),
|
||||
(float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()),
|
||||
(float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ()));
|
||||
return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()), (float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()), (float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ()));
|
||||
}
|
||||
|
||||
public boolean isVisible(FrustumIntersection frustum) {
|
||||
if (entity.noCulling) {
|
||||
return true;
|
||||
}
|
||||
return boxTracker.isVisible(frustum);
|
||||
public boolean visible(FrustumIntersection frustum) {
|
||||
return entity.noCulling || visibilityTester.check(frustum);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,13 @@ public class EntityVisibilityTester {
|
|||
this.renderOrigin = renderOrigin;
|
||||
}
|
||||
|
||||
public boolean isVisible(FrustumIntersection frustum) {
|
||||
/**
|
||||
* Check whether the Entity is visible.
|
||||
*
|
||||
* @param frustum The frustum to test against.
|
||||
* @return {@code true} if the Entity is visible, {@code false} otherwise.
|
||||
*/
|
||||
public boolean check(FrustumIntersection frustum) {
|
||||
AABB aabb = entity.getBoundingBoxForCulling();
|
||||
|
||||
boolean visible = adjustAndTestAABB(frustum, aabb);
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||
import com.jozufozu.flywheel.lib.instance.OrientedInstance;
|
||||
|
@ -38,11 +39,21 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
|
|||
bell = createBellInstance().setPivot(0.5f, 0.75f, 0.5f)
|
||||
.setPosition(getVisualPosition());
|
||||
|
||||
updateRotation();
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame() {
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
if (doDistanceLimitThisFrame(context) || !visible(context.frustum())) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateRotation();
|
||||
}
|
||||
|
||||
private void updateRotation() {
|
||||
float ringTime = (float) blockEntity.ticks + AnimationTickHolder.getPartialTicks();
|
||||
|
||||
if (ringTime == lastRingTime) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.function.BiFunction;
|
|||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||
import com.jozufozu.flywheel.lib.instance.OrientedInstance;
|
||||
|
@ -80,7 +81,11 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame() {
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
if (doDistanceLimitThisFrame(context) || !visible(context.frustum())) {
|
||||
return;
|
||||
}
|
||||
|
||||
float progress = lidProgress.get(AnimationTickHolder.getPartialTicks());
|
||||
|
||||
if (lastProgress == progress) {
|
||||
|
@ -96,7 +101,7 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
|
||||
lid.loadIdentity()
|
||||
.translate(getVisualPosition())
|
||||
.translate(0, 9f/16f, 0)
|
||||
.translate(0, 9f / 16f, 0)
|
||||
.centre()
|
||||
.multiply(baseRotation)
|
||||
.unCentre()
|
||||
|
|
|
@ -5,6 +5,8 @@ import org.jetbrains.annotations.NotNull;
|
|||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visual.VisualTickContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||
import com.jozufozu.flywheel.lib.instance.TransformedInstance;
|
||||
|
@ -44,11 +46,13 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
|
|||
blockState = entity.getDisplayBlockState();
|
||||
contents = createContentsInstance();
|
||||
|
||||
updatePosition();
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
public void tick(VisualTickContext c) {
|
||||
BlockState displayBlockState = entity.getDisplayBlockState();
|
||||
|
||||
if (displayBlockState != blockState) {
|
||||
|
@ -62,12 +66,19 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
|
|||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame() {
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
if (visible(context.frustum())) {
|
||||
return;
|
||||
}
|
||||
// TODO: add proper way to temporarily disable rendering a specific instance
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
private void updatePosition() {
|
||||
TransformStack tstack = TransformStack.cast(stack);
|
||||
stack.setIdentity();
|
||||
float pt = AnimationTickHolder.getPartialTicks();
|
||||
|
@ -76,7 +87,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
|
|||
|
||||
float yaw = Mth.lerp(pt, entity.yRotO, entity.getYRot());
|
||||
|
||||
long i = (long)entity.getId() * 493286711L;
|
||||
long i = (long) entity.getId() * 493286711L;
|
||||
i = i * i * 4392167121L + i * 98761L;
|
||||
float f = (((float)(i >> 16 & 7L) + 0.5F) / 8 - 0.5F) * 0.004F;
|
||||
float f1 = (((float)(i >> 20 & 7L) + 0.5F) / 8 - 0.5F) * 0.004F;
|
||||
|
@ -134,11 +145,6 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
|
|||
body.setTransform(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean decreaseFramerateWithDistance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLight() {
|
||||
if (contents == null) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.util.function.Function;
|
|||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||
import com.jozufozu.flywheel.lib.instance.TransformedInstance;
|
||||
|
@ -81,10 +82,15 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
|
|||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame() {
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
if (doDistanceLimitThisFrame(context) || !visible(context.frustum())) {
|
||||
return;
|
||||
}
|
||||
float progress = blockEntity.getProgress(AnimationTickHolder.getPartialTicks());
|
||||
|
||||
if (progress == lastProgress) return;
|
||||
if (progress == lastProgress) {
|
||||
return;
|
||||
}
|
||||
lastProgress = progress;
|
||||
|
||||
Quaternion spin = Vector3f.YP.rotationDegrees(270.0F * progress);
|
||||
|
|
|
@ -5,7 +5,6 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||
|
@ -14,6 +13,8 @@ import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
|||
import com.jozufozu.flywheel.api.visual.Effect;
|
||||
import com.jozufozu.flywheel.api.visual.EffectVisual;
|
||||
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visual.VisualTickContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.impl.visualization.VisualizedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
||||
|
@ -268,12 +269,12 @@ public class ExampleEffect implements Effect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
public void tick(VisualTickContext c) {
|
||||
self.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame() {
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
float partialTicks = AnimationTickHolder.getPartialTicks();
|
||||
|
||||
var x = Mth.lerp(partialTicks, self.lastPosition.x, self.position.x);
|
||||
|
@ -285,25 +286,5 @@ public class ExampleEffect implements Effect {
|
|||
.translate(x, y, z)
|
||||
.scale(RENDER_SCALE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean decreaseTickRateWithDistance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean decreaseFramerateWithDistance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(FrustumIntersection frustum) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distanceSquared(double x, double y, double z) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue