mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 23:47:09 +01:00
Plans yet to crumble
- Rename Engine#delete -> #invalidate per pepper's TODO. - Plans - Remove thenMap and andMap from Plan API. - Add builder for MapContextPlan for better composition. - Add IfElsePlan and Builder to "fork" on a condition. - VisualizationManagerImpl no longer rolls a special Plan class and instead uses a plan composition chain. - Crumbling - Not implemented yet!! But the skeleton is taking shape. - Remove LevelRendererAccessor, and instead directly pass the map of destructionProgress to the VisualizationManagerImpl when it's time to render crumbling instances. - Give Instances a Handle getter. - Add way to get a block entity visual at a given position. - Add Engine#renderCrumblingInstance stub.
This commit is contained in:
parent
e5cda8944e
commit
055ac161d8
19 changed files with 205 additions and 105 deletions
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.api.backend;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
|
@ -14,6 +15,8 @@ public interface Engine extends InstancerProvider {
|
||||||
|
|
||||||
void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage);
|
void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage);
|
||||||
|
|
||||||
|
void renderCrumblingInstance(TaskExecutor taskExecutor, RenderContext context, Instance instance, int progress);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maintain the render origin to be within a certain distance from the camera in all directions,
|
* Maintain the render origin to be within a certain distance from the camera in all directions,
|
||||||
* preventing floating point precision issues at high coordinates.
|
* preventing floating point precision issues at high coordinates.
|
||||||
|
@ -24,7 +27,5 @@ public interface Engine extends InstancerProvider {
|
||||||
|
|
||||||
Vec3i renderOrigin();
|
Vec3i renderOrigin();
|
||||||
|
|
||||||
// TODO: "delete" implies that the object cannot be used afterwards, but all current implementations
|
void invalidate();
|
||||||
// support the "invalidate" contract as well, meaning they can be reused after this call. Rename?
|
|
||||||
void delete();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,6 @@ package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
public interface Instance {
|
public interface Instance {
|
||||||
InstanceType<?> type();
|
InstanceType<?> type();
|
||||||
|
|
||||||
|
InstanceHandle handle();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,16 +33,6 @@ public interface Plan<C> {
|
||||||
*/
|
*/
|
||||||
Plan<C> then(Plan<C> plan);
|
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.
|
* Create a new plan that executes this plan and the given plan in parallel.
|
||||||
*
|
*
|
||||||
|
@ -51,16 +41,6 @@ public interface Plan<C> {
|
||||||
*/
|
*/
|
||||||
Plan<C> and(Plan<C> plan);
|
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
|
* If possible, create a new plan that accomplishes everything
|
||||||
* this plan does but with a simpler execution schedule.
|
* this plan does but with a simpler execution schedule.
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.jozufozu.flywheel.backend.engine;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceHandle;
|
import com.jozufozu.flywheel.api.instance.InstanceHandle;
|
||||||
|
|
||||||
public class InstanceHandleImpl implements InstanceHandle {
|
public class InstanceHandleImpl implements InstanceHandle {
|
||||||
private final AbstractInstancer<?> instancer;
|
public final AbstractInstancer<?> instancer;
|
||||||
private int index;
|
private int index;
|
||||||
|
|
||||||
public InstanceHandleImpl(AbstractInstancer<?> instancer, int index) {
|
public InstanceHandleImpl(AbstractInstancer<?> instancer, int index) {
|
||||||
|
|
|
@ -93,13 +93,18 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan
|
||||||
drawTracker.draw(stage);
|
drawTracker.draw(stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderCrumblingInstance(TaskExecutor taskExecutor, RenderContext context, Instance instance, int progress) {
|
||||||
|
// TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onRenderOriginChanged() {
|
protected void onRenderOriginChanged() {
|
||||||
initializedInstancers.forEach(BatchedInstancer::clear);
|
initializedInstancers.forEach(BatchedInstancer::clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void invalidate() {
|
||||||
instancers.clear();
|
instancers.clear();
|
||||||
|
|
||||||
meshPools.values()
|
meshPools.values()
|
||||||
|
|
|
@ -68,6 +68,11 @@ public class IndirectEngine extends AbstractEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderCrumblingInstance(TaskExecutor taskExecutor, RenderContext context, Instance instance, int progress) {
|
||||||
|
// TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
private void setup() {
|
private void setup() {
|
||||||
GlTextureUnit.T2.makeActive();
|
GlTextureUnit.T2.makeActive();
|
||||||
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
||||||
|
@ -85,7 +90,7 @@ public class IndirectEngine extends AbstractEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void invalidate() {
|
||||||
drawManager.invalidate();
|
drawManager.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,11 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderCrumblingInstance(TaskExecutor taskExecutor, RenderContext context, Instance instance, int progress) {
|
||||||
|
// TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
private void setup() {
|
private void setup() {
|
||||||
GlTextureUnit.T2.makeActive();
|
GlTextureUnit.T2.makeActive();
|
||||||
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
||||||
|
@ -97,7 +102,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(shader);
|
setup(shader, context);
|
||||||
|
|
||||||
shader.material().setup();
|
shader.material().setup();
|
||||||
|
|
||||||
|
@ -109,7 +114,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup(ShaderState desc) {
|
private void setup(ShaderState desc, Context context) {
|
||||||
var material = desc.material();
|
var material = desc.material();
|
||||||
var vertexType = desc.vertexType();
|
var vertexType = desc.vertexType();
|
||||||
var instanceType = desc.instanceType();
|
var instanceType = desc.instanceType();
|
||||||
|
@ -130,7 +135,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void invalidate() {
|
||||||
drawManager.invalidate();
|
drawManager.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import net.minecraft.core.Vec3i;
|
||||||
|
|
||||||
public record FrameContext(double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum, float partialTick) {
|
public record FrameContext(double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum, float partialTick) {
|
||||||
@NotNull
|
@NotNull
|
||||||
public static FrameContext create(RenderContext context, Vec3i renderOrigin, float partialTick) {
|
public static FrameContext create(RenderContext context, Vec3i renderOrigin) {
|
||||||
var cameraPos = context.camera()
|
var cameraPos = context.camera()
|
||||||
.getPosition();
|
.getPosition();
|
||||||
double cameraX = cameraPos.x;
|
double cameraX = cameraPos.x;
|
||||||
|
@ -21,6 +21,6 @@ public record FrameContext(double cameraX, double cameraY, double cameraZ, Frust
|
||||||
viewProjection.translate((float) (renderOrigin.getX() - cameraX), (float) (renderOrigin.getY() - cameraY), (float) (renderOrigin.getZ() - cameraZ));
|
viewProjection.translate((float) (renderOrigin.getX() - cameraX), (float) (renderOrigin.getY() - cameraY), (float) (renderOrigin.getZ() - cameraZ));
|
||||||
FrustumIntersection frustum = new FrustumIntersection(viewProjection);
|
FrustumIntersection frustum = new FrustumIntersection(viewProjection);
|
||||||
|
|
||||||
return new FrameContext(cameraX, cameraY, cameraZ, frustum, partialTick);
|
return new FrameContext(cameraX, cameraY, cameraZ, frustum, context.partialTick());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package com.jozufozu.flywheel.impl.visualization;
|
package com.jozufozu.flywheel.impl.visualization;
|
||||||
|
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||||
|
@ -20,15 +23,17 @@ import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager
|
||||||
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
|
||||||
import com.jozufozu.flywheel.impl.visualization.manager.EntityVisualManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.EntityVisualManager;
|
||||||
import com.jozufozu.flywheel.lib.task.Flag;
|
import com.jozufozu.flywheel.lib.task.Flag;
|
||||||
|
import com.jozufozu.flywheel.lib.task.MapContextPlan;
|
||||||
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
||||||
import com.jozufozu.flywheel.lib.task.NestedPlan;
|
|
||||||
import com.jozufozu.flywheel.lib.task.RaisePlan;
|
import com.jozufozu.flywheel.lib.task.RaisePlan;
|
||||||
import com.jozufozu.flywheel.lib.task.SimplyComposedPlan;
|
import com.jozufozu.flywheel.lib.task.IfElsePlan;
|
||||||
import com.jozufozu.flywheel.lib.util.LevelAttached;
|
import com.jozufozu.flywheel.lib.util.LevelAttached;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
|
import net.minecraft.server.level.BlockDestructionProgress;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
@ -50,7 +55,8 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
||||||
private final Plan<RenderContext> framePlan;
|
private final Plan<RenderContext> framePlan;
|
||||||
|
|
||||||
private final Flag tickFlag = new NamedFlag("tick");
|
private final Flag tickFlag = new NamedFlag("tick");
|
||||||
private final Flag frameFlag = new NamedFlag("frame");
|
private final Flag frameVisualsFlag = new NamedFlag("frameVisualUpdates");
|
||||||
|
private final Flag frameFlag = new NamedFlag("frameComplete");
|
||||||
|
|
||||||
private VisualizationManagerImpl(LevelAccessor level) {
|
private VisualizationManagerImpl(LevelAccessor level) {
|
||||||
engine = BackendManager.getBackend()
|
engine = BackendManager.getBackend()
|
||||||
|
@ -66,7 +72,21 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
||||||
.and(effects.createTickPlan())
|
.and(effects.createTickPlan())
|
||||||
.then(RaisePlan.raise(tickFlag))
|
.then(RaisePlan.raise(tickFlag))
|
||||||
.simplify();
|
.simplify();
|
||||||
framePlan = new FramePlan().then(RaisePlan.raise(frameFlag));
|
|
||||||
|
framePlan = IfElsePlan.on((RenderContext ctx) -> engine.updateRenderOrigin(ctx.camera()))
|
||||||
|
.ifTrue(MapContextPlan.map(RenderContext::partialTick)
|
||||||
|
.to(blockEntities.createRecreationPlan()
|
||||||
|
.and(entities.createRecreationPlan())
|
||||||
|
.and(effects.createRecreationPlan())))
|
||||||
|
.ifFalse(MapContextPlan.map((RenderContext ctx) -> FrameContext.create(ctx, engine.renderOrigin()))
|
||||||
|
.to(blockEntities.createFramePlan()
|
||||||
|
.and(entities.createFramePlan())
|
||||||
|
.and(effects.createFramePlan())))
|
||||||
|
.plan()
|
||||||
|
.then(RaisePlan.raise(frameVisualsFlag))
|
||||||
|
.then(engine.createFramePlan())
|
||||||
|
.then(RaisePlan.raise(frameFlag))
|
||||||
|
.simplify();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean supportsVisualization(@Nullable LevelAccessor level) {
|
public static boolean supportsVisualization(@Nullable LevelAccessor level) {
|
||||||
|
@ -171,6 +191,8 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
||||||
// Note we don't lower here because many frames may happen per tick.
|
// Note we don't lower here because many frames may happen per tick.
|
||||||
taskExecutor.syncUntil(tickFlag::isRaised);
|
taskExecutor.syncUntil(tickFlag::isRaised);
|
||||||
|
|
||||||
|
frameVisualsFlag.lower();
|
||||||
|
frameFlag.lower();
|
||||||
framePlan.execute(taskExecutor, context);
|
framePlan.execute(taskExecutor, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +203,41 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
||||||
engine.renderStage(taskExecutor, context, stage);
|
engine.renderStage(taskExecutor, context, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void renderCrumbling(RenderContext context, Long2ObjectMap<SortedSet<BlockDestructionProgress>> destructionProgress) {
|
||||||
|
taskExecutor.syncUntil(frameVisualsFlag::isRaised);
|
||||||
|
|
||||||
|
for (var entry : destructionProgress.long2ObjectEntrySet()) {
|
||||||
|
var set = entry.getValue();
|
||||||
|
if (set == null || set.isEmpty()) {
|
||||||
|
// Nothing to do if there's no crumbling.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var visual = blockEntities.visualAtPos(entry.getLongKey());
|
||||||
|
|
||||||
|
if (visual == null) {
|
||||||
|
// The block doesn't have a visual, this is probably the common case.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var instanceList = visual.getCrumblingInstances();
|
||||||
|
|
||||||
|
if (instanceList.isEmpty()) {
|
||||||
|
// The visual doesn't want to render anything crumbling.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now for the fun part
|
||||||
|
|
||||||
|
int progress = set.last()
|
||||||
|
.getProgress();
|
||||||
|
|
||||||
|
for (Instance instance : instanceList) {
|
||||||
|
engine.renderCrumblingInstance(taskExecutor, context, instance, progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free all acquired resources and delete this manager.
|
* Free all acquired resources and delete this manager.
|
||||||
*/
|
*/
|
||||||
|
@ -192,29 +249,6 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
||||||
blockEntities.invalidate();
|
blockEntities.invalidate();
|
||||||
entities.invalidate();
|
entities.invalidate();
|
||||||
effects.invalidate();
|
effects.invalidate();
|
||||||
engine.delete();
|
engine.invalidate();
|
||||||
}
|
|
||||||
|
|
||||||
private class FramePlan implements SimplyComposedPlan<RenderContext> {
|
|
||||||
private final Plan<Float> recreationPlan = NestedPlan.of(blockEntities.createRecreationPlan(), entities.createRecreationPlan(), effects.createRecreationPlan());
|
|
||||||
private final Plan<FrameContext> normalPlan = blockEntities.createFramePlan()
|
|
||||||
.and(entities.createFramePlan())
|
|
||||||
.and(effects.createFramePlan());
|
|
||||||
|
|
||||||
private final Plan<RenderContext> enginePlan = engine.createFramePlan();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(TaskExecutor taskExecutor, RenderContext context, Runnable onCompletion) {
|
|
||||||
Runnable then = () -> enginePlan.execute(taskExecutor, context, onCompletion);
|
|
||||||
float partialTick = context.partialTick();
|
|
||||||
|
|
||||||
if (engine.updateRenderOrigin(context.camera())) {
|
|
||||||
recreationPlan.execute(taskExecutor, partialTick, then);
|
|
||||||
} else {
|
|
||||||
var frameContext = FrameContext.create(context, engine.renderOrigin(), partialTick);
|
|
||||||
|
|
||||||
normalPlan.execute(taskExecutor, frameContext, then);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import com.jozufozu.flywheel.impl.visualization.ratelimit.DistanceUpdateLimiterI
|
||||||
import com.jozufozu.flywheel.impl.visualization.ratelimit.NonLimiter;
|
import com.jozufozu.flywheel.impl.visualization.ratelimit.NonLimiter;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Transaction;
|
import com.jozufozu.flywheel.impl.visualization.storage.Transaction;
|
||||||
|
import com.jozufozu.flywheel.lib.task.MapContextPlan;
|
||||||
import com.jozufozu.flywheel.lib.task.SimplePlan;
|
import com.jozufozu.flywheel.lib.task.SimplePlan;
|
||||||
|
|
||||||
public abstract class AbstractVisualManager<T> implements VisualManager<T> {
|
public abstract class AbstractVisualManager<T> implements VisualManager<T> {
|
||||||
|
@ -88,7 +89,8 @@ public abstract class AbstractVisualManager<T> implements VisualManager<T> {
|
||||||
tickLimiter.tick();
|
tickLimiter.tick();
|
||||||
processQueue(0);
|
processQueue(0);
|
||||||
})
|
})
|
||||||
.thenMap(this::createVisualTickContext, getStorage().getTickPlan());
|
.then(MapContextPlan.map(this::createVisualTickContext)
|
||||||
|
.to(getStorage().getTickPlan()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plan<FrameContext> createFramePlan() {
|
public Plan<FrameContext> createFramePlan() {
|
||||||
|
@ -96,7 +98,8 @@ public abstract class AbstractVisualManager<T> implements VisualManager<T> {
|
||||||
frameLimiter.tick();
|
frameLimiter.tick();
|
||||||
processQueue(context.partialTick());
|
processQueue(context.partialTick());
|
||||||
})
|
})
|
||||||
.thenMap(this::createVisualContext, getStorage().getFramePlan());
|
.then(MapContextPlan.map(this::createVisualContext)
|
||||||
|
.to(getStorage().getFramePlan()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private VisualFrameContext createVisualContext(FrameContext ctx) {
|
private VisualFrameContext createVisualContext(FrameContext ctx) {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package com.jozufozu.flywheel.impl.visualization.manager;
|
package com.jozufozu.flywheel.impl.visualization.manager;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
|
@ -30,11 +28,8 @@ public class BlockEntityVisualManager extends AbstractVisualManager<BlockEntity>
|
||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getCrumblingVisuals(long pos, List<BlockEntityVisual<?>> visuals) {
|
public BlockEntityVisual<?> visualAtPos(long pos) {
|
||||||
BlockEntityVisual<?> visual = storage.posLookup.get(pos);
|
return storage.posLookup.get(pos);
|
||||||
if (visual != null) {
|
|
||||||
visuals.add(visual);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class BlockEntityStorage extends Storage<BlockEntity> {
|
private static class BlockEntityStorage extends Storage<BlockEntity> {
|
||||||
|
|
|
@ -18,6 +18,11 @@ public abstract class AbstractInstance implements Instance {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstanceHandle handle() {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
public final void setChanged() {
|
public final void setChanged() {
|
||||||
handle.setChanged();
|
handle.setChanged();
|
||||||
}
|
}
|
||||||
|
|
65
src/main/java/com/jozufozu/flywheel/lib/task/IfElsePlan.java
Normal file
65
src/main/java/com/jozufozu/flywheel/lib/task/IfElsePlan.java
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package com.jozufozu.flywheel.lib.task;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes one plan or another, depending on a dynamically evaluated condition.
|
||||||
|
* @param condition The condition to branch on.
|
||||||
|
* @param onTrue The plan to execute if the condition is true.
|
||||||
|
* @param onFalse The plan to execute if the condition is false.
|
||||||
|
* @param <C> The type of the context object.
|
||||||
|
*/
|
||||||
|
public record IfElsePlan<C>(Predicate<C> condition, Plan<C> onTrue, Plan<C> onFalse) implements SimplyComposedPlan<C> {
|
||||||
|
public static <C> Builder<C> on(Predicate<C> condition) {
|
||||||
|
return new Builder<>(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(TaskExecutor taskExecutor, C context, Runnable onCompletion) {
|
||||||
|
if (condition.test(context)) {
|
||||||
|
onTrue.execute(taskExecutor, context, onCompletion);
|
||||||
|
} else {
|
||||||
|
onFalse.execute(taskExecutor, context, onCompletion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Plan<C> simplify() {
|
||||||
|
var maybeSimplifiedTrue = onTrue.simplify();
|
||||||
|
var maybeSimplifiedFalse = onFalse.simplify();
|
||||||
|
|
||||||
|
if (maybeSimplifiedTrue instanceof UnitPlan && maybeSimplifiedFalse instanceof UnitPlan) {
|
||||||
|
// The condition may have side effects that still need to be evaluated.
|
||||||
|
return SimplePlan.of(condition::test);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IfElsePlan<>(condition, maybeSimplifiedTrue, maybeSimplifiedFalse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder<C> {
|
||||||
|
private final Predicate<C> condition;
|
||||||
|
private Plan<C> onTrue = UnitPlan.of();
|
||||||
|
private Plan<C> onFalse = UnitPlan.of();
|
||||||
|
|
||||||
|
public Builder(Predicate<C> condition) {
|
||||||
|
this.condition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<C> ifTrue(Plan<C> onTrue) {
|
||||||
|
this.onTrue = onTrue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<C> ifFalse(Plan<C> onFalse) {
|
||||||
|
this.onFalse = onFalse;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IfElsePlan<C> plan() {
|
||||||
|
return new IfElsePlan<>(condition, onTrue, onFalse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,10 @@ import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
|
|
||||||
public record MapContextPlan<C, D>(Function<C, D> map, Plan<D> plan) implements SimplyComposedPlan<C> {
|
public record MapContextPlan<C, D>(Function<C, D> map, Plan<D> plan) implements SimplyComposedPlan<C> {
|
||||||
|
public static <C, D> Builder<C, D> map(Function<C, D> map) {
|
||||||
|
return new Builder<>(map);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(TaskExecutor taskExecutor, C context, Runnable onCompletion) {
|
public void execute(TaskExecutor taskExecutor, C context, Runnable onCompletion) {
|
||||||
D newContext = map.apply(context);
|
D newContext = map.apply(context);
|
||||||
|
@ -22,4 +26,20 @@ public record MapContextPlan<C, D>(Function<C, D> map, Plan<D> plan) implements
|
||||||
|
|
||||||
return new MapContextPlan<>(map, maybeSimplified);
|
return new MapContextPlan<>(map, maybeSimplified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Builder<C, D> {
|
||||||
|
private final Function<C, D> map;
|
||||||
|
|
||||||
|
public Builder(Function<C, D> map) {
|
||||||
|
this.map = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapContextPlan<C, D> to(Plan<D> plan) {
|
||||||
|
return new MapContextPlan<>(map, plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapContextPlan<C, D> plan() {
|
||||||
|
return new MapContextPlan<>(map, UnitPlan.of());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,21 +10,11 @@ public interface SimplyComposedPlan<C> extends Plan<C> {
|
||||||
return new BarrierPlan<>(this, plan);
|
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
|
@Override
|
||||||
default Plan<C> and(Plan<C> plan) {
|
default Plan<C> and(Plan<C> plan) {
|
||||||
return NestedPlan.of(this, 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
|
@Override
|
||||||
default Plan<C> simplify() {
|
default Plan<C> simplify() {
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -26,21 +26,11 @@ public class UnitPlan<C> implements Plan<C> {
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <D> Plan<C> thenMap(Function<C, D> map, Plan<D> plan) {
|
|
||||||
return new MapContextPlan<>(map, plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Plan<C> and(Plan<C> plan) {
|
public Plan<C> and(Plan<C> plan) {
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <D> Plan<C> andMap(Function<C, D> map, Plan<D> plan) {
|
|
||||||
return new MapContextPlan<>(map, plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Plan<C> simplify() {
|
public Plan<C> simplify() {
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
package com.jozufozu.flywheel.mixin;
|
|
||||||
|
|
||||||
import java.util.SortedSet;
|
|
||||||
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
|
||||||
import net.minecraft.server.level.BlockDestructionProgress;
|
|
||||||
|
|
||||||
@Mixin(LevelRenderer.class)
|
|
||||||
public interface LevelRendererAccessor {
|
|
||||||
@Accessor("destructionProgress")
|
|
||||||
Long2ObjectMap<SortedSet<BlockDestructionProgress>> flywheel$getDestructionProgress();
|
|
||||||
}
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.jozufozu.flywheel.mixin;
|
package com.jozufozu.flywheel.mixin;
|
||||||
|
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
@ -16,14 +18,17 @@ import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStageEvent;
|
import com.jozufozu.flywheel.api.event.RenderStageEvent;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.VisualizationManagerImpl;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.client.renderer.GameRenderer;
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
import net.minecraft.client.renderer.LevelRenderer;
|
||||||
import net.minecraft.client.renderer.LightTexture;
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
import net.minecraft.client.renderer.RenderBuffers;
|
import net.minecraft.client.renderer.RenderBuffers;
|
||||||
|
import net.minecraft.server.level.BlockDestructionProgress;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
@Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after Sodium
|
@Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after Sodium
|
||||||
|
@ -35,6 +40,10 @@ public class LevelRendererMixin {
|
||||||
@Final
|
@Final
|
||||||
private RenderBuffers renderBuffers;
|
private RenderBuffers renderBuffers;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private Long2ObjectMap<SortedSet<BlockDestructionProgress>> destructionProgress;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private RenderContext flywheel$renderContext;
|
private RenderContext flywheel$renderContext;
|
||||||
|
|
||||||
|
@ -142,4 +151,12 @@ public class LevelRendererMixin {
|
||||||
private void flywheel$onStage$afterWeather(CallbackInfo ci) {
|
private void flywheel$onStage$afterWeather(CallbackInfo ci) {
|
||||||
flywheel$dispatch(RenderStage.AFTER_WEATHER);
|
flywheel$dispatch(RenderStage.AFTER_WEATHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "renderLevel", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", args = "ldc=destroyProgress"))
|
||||||
|
private void flywheel$crumbling(CallbackInfo ci) {
|
||||||
|
var vm = VisualizationManagerImpl.get(level);
|
||||||
|
if (vm != null) {
|
||||||
|
vm.renderCrumbling(flywheel$renderContext, destructionProgress);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
"EntityTypeMixin",
|
"EntityTypeMixin",
|
||||||
"FogUpdateMixin",
|
"FogUpdateMixin",
|
||||||
"GlStateManagerMixin",
|
"GlStateManagerMixin",
|
||||||
"LevelRendererAccessor",
|
|
||||||
"LevelRendererMixin",
|
"LevelRendererMixin",
|
||||||
"LightUpdateMixin",
|
"LightUpdateMixin",
|
||||||
"RenderTypeMixin",
|
"RenderTypeMixin",
|
||||||
|
|
Loading…
Reference in a new issue