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:
Jozufozu 2023-11-24 20:40:14 -08:00
parent e5cda8944e
commit 055ac161d8
19 changed files with 205 additions and 105 deletions

View file

@ -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();
} }

View file

@ -2,4 +2,6 @@ package com.jozufozu.flywheel.api.instance;
public interface Instance { public interface Instance {
InstanceType<?> type(); InstanceType<?> type();
InstanceHandle handle();
} }

View file

@ -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.

View file

@ -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) {

View file

@ -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()

View file

@ -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();
} }
} }

View file

@ -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();
} }
} }

View file

@ -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());
} }
} }

View file

@ -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);
}
}
} }
} }

View file

@ -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) {

View file

@ -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> {

View file

@ -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();
} }

View 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);
}
}
}

View file

@ -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());
}
}
} }

View file

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

View file

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

View file

@ -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();
}

View file

@ -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);
}
}
} }

View file

@ -11,7 +11,6 @@
"EntityTypeMixin", "EntityTypeMixin",
"FogUpdateMixin", "FogUpdateMixin",
"GlStateManagerMixin", "GlStateManagerMixin",
"LevelRendererAccessor",
"LevelRendererMixin", "LevelRendererMixin",
"LightUpdateMixin", "LightUpdateMixin",
"RenderTypeMixin", "RenderTypeMixin",