Separate LightVolume and GPULightVolume

- LightVolumes now can act as a light cache with configurable size
 - More GridAlignedBB changes
 - Remove ILightUpdateListeners
 - Simplify pulley rendering using LightVolume
This commit is contained in:
Jozufozu 2021-09-08 15:48:49 -07:00
parent d13bf42c22
commit 6fcc960189
9 changed files with 100 additions and 143 deletions

View file

@ -27,7 +27,7 @@ import org.apache.commons.lang3.tuple.Pair;
import com.jozufozu.flywheel.backend.IFlywheelWorld; import com.jozufozu.flywheel.backend.IFlywheelWorld;
import com.jozufozu.flywheel.light.GridAlignedBB; import com.jozufozu.flywheel.light.GridAlignedBB;
import com.jozufozu.flywheel.light.ReadOnlyBox; import com.jozufozu.flywheel.light.ImmutableBox;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllMovementBehaviours; import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.IRotate;
@ -1143,7 +1143,7 @@ public abstract class Contraption {
GridAlignedBB betterBounds = GridAlignedBB.ofRadius(radius); GridAlignedBB betterBounds = GridAlignedBB.ofRadius(radius);
ReadOnlyBox contraptionBounds = GridAlignedBB.from(bounds); ImmutableBox contraptionBounds = GridAlignedBB.from(bounds);
if (axis == Direction.Axis.X) { if (axis == Direction.Axis.X) {
betterBounds.setMaxX(contraptionBounds.getMaxX()); betterBounds.setMaxX(contraptionBounds.getMaxX());
betterBounds.setMinX(contraptionBounds.getMinX()); betterBounds.setMinX(contraptionBounds.getMinX());

View file

@ -6,10 +6,10 @@ import net.minecraft.world.LightType;
public abstract class ContraptionLighter<C extends Contraption> implements ILightUpdateListener { public abstract class ContraptionLighter<C extends Contraption> implements ILightUpdateListener {
protected final C contraption; protected final C contraption;
public final LightVolume lightVolume; public final GPULightVolume lightVolume;
protected final LightUpdater lightUpdater; protected final LightUpdater lightUpdater;
protected GridAlignedBB bounds; protected final GridAlignedBB bounds;
protected boolean scheduleRebuild; protected boolean scheduleRebuild;
@ -18,15 +18,14 @@ public abstract class ContraptionLighter<C extends Contraption> implements ILigh
lightUpdater = LightUpdater.get(contraption.entity.level); lightUpdater = LightUpdater.get(contraption.entity.level);
bounds = getContraptionBounds(); bounds = getContraptionBounds();
growBoundsForEdgeData();
lightVolume = new LightVolume(contraptionBoundsToVolume(bounds)); lightVolume = new GPULightVolume(bounds);
lightVolume.initialize(contraption.entity.level); lightVolume.initialize(lightUpdater.getProvider());
scheduleRebuild = true; scheduleRebuild = true;
lightUpdater.addListener(this); lightUpdater.addListener(this);
lightVolume.initialize(this.contraption.entity.level);
} }
public abstract GridAlignedBB getContraptionBounds(); public abstract GridAlignedBB getContraptionBounds();
@ -37,26 +36,28 @@ public abstract class ContraptionLighter<C extends Contraption> implements ILigh
} }
@Override @Override
public void onLightUpdate(LightProvider world, LightType type, ReadOnlyBox changed) { public void onLightUpdate(LightProvider world, LightType type, ImmutableBox changed) {
lightVolume.notifyLightUpdate(world, type, changed); lightVolume.onLightUpdate(world, type, changed);
} }
@Override @Override
public void onLightPacket(LightProvider world, int chunkX, int chunkZ) { public void onLightPacket(LightProvider world, int chunkX, int chunkZ) {
lightVolume.notifyLightPacket(world, chunkX, chunkZ); lightVolume.onLightPacket(world, chunkX, chunkZ);
} }
protected GridAlignedBB contraptionBoundsToVolume(ReadOnlyBox box) { protected void growBoundsForEdgeData() {
GridAlignedBB bounds = box.copy();
bounds.grow(2); // so we have at least enough data on the edges to avoid artifacts and have smooth lighting bounds.grow(2); // so we have at least enough data on the edges to avoid artifacts and have smooth lighting
bounds.setMinY(Math.max(bounds.getMinY(), 0)); bounds.setMinY(Math.max(bounds.getMinY(), 0));
bounds.setMaxY(Math.min(bounds.getMaxY(), 255)); bounds.setMaxY(Math.min(bounds.getMaxY(), 255));
}
return bounds;
}
@Override @Override
public ReadOnlyBox getVolume() { public ImmutableBox getVolume() {
return bounds; return bounds;
} }
public void delete() {
lightUpdater.removeListener(this);
lightVolume.delete();
}
} }

View file

@ -3,7 +3,7 @@ package com.simibubi.create.content.contraptions.components.structureMovement;
import com.jozufozu.flywheel.light.GridAlignedBB; import com.jozufozu.flywheel.light.GridAlignedBB;
import com.jozufozu.flywheel.light.IMovingListener; import com.jozufozu.flywheel.light.IMovingListener;
import com.jozufozu.flywheel.light.LightProvider; import com.jozufozu.flywheel.light.LightProvider;
import com.jozufozu.flywheel.light.ReadOnlyBox; import com.jozufozu.flywheel.light.ImmutableBox;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
public class NonStationaryLighter<C extends Contraption> extends ContraptionLighter<C> implements IMovingListener { public class NonStationaryLighter<C extends Contraption> extends ContraptionLighter<C> implements IMovingListener {
@ -16,14 +16,15 @@ public class NonStationaryLighter<C extends Contraption> extends ContraptionLigh
if (getVolume().volume() > AllConfigs.CLIENT.maxContraptionLightVolume.get()) if (getVolume().volume() > AllConfigs.CLIENT.maxContraptionLightVolume.get())
return false; return false;
ReadOnlyBox contraptionBounds = getContraptionBounds(); ImmutableBox contraptionBounds = getContraptionBounds();
if (bounds.sameAs(contraptionBounds)) { if (bounds.sameAs(contraptionBounds)) {
return false; return false;
} }
bounds.assign(contraptionBounds); bounds.assign(contraptionBounds);
lightVolume.move(contraption.entity.level, contraptionBoundsToVolume(bounds)); growBoundsForEdgeData();
lightVolume.move(provider, bounds);
return true; return true;
} }

View file

@ -1,7 +1,5 @@
package com.simibubi.create.content.contraptions.components.structureMovement.pulley; package com.simibubi.create.content.contraptions.components.structureMovement.pulley;
import java.util.Arrays;
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance; import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
import com.jozufozu.flywheel.backend.instancing.Instancer; import com.jozufozu.flywheel.backend.instancing.Instancer;
import com.jozufozu.flywheel.backend.material.MaterialManager; import com.jozufozu.flywheel.backend.material.MaterialManager;
@ -9,23 +7,21 @@ import com.jozufozu.flywheel.core.instancing.ConditionalInstance;
import com.jozufozu.flywheel.core.instancing.GroupInstance; import com.jozufozu.flywheel.core.instancing.GroupInstance;
import com.jozufozu.flywheel.core.instancing.SelectInstance; import com.jozufozu.flywheel.core.instancing.SelectInstance;
import com.jozufozu.flywheel.core.materials.OrientedData; import com.jozufozu.flywheel.core.materials.OrientedData;
import com.jozufozu.flywheel.light.BasicProvider;
import com.jozufozu.flywheel.light.GridAlignedBB; import com.jozufozu.flywheel.light.GridAlignedBB;
import com.jozufozu.flywheel.light.IMovingListener; import com.jozufozu.flywheel.light.IMovingListener;
import com.jozufozu.flywheel.light.LightProvider; import com.jozufozu.flywheel.light.LightProvider;
import com.jozufozu.flywheel.light.ListenerStatus; import com.jozufozu.flywheel.light.ImmutableBox;
import com.jozufozu.flywheel.light.ReadOnlyBox; import com.jozufozu.flywheel.light.LightUpdater;
import com.jozufozu.flywheel.light.LightVolume;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.relays.encased.ShaftInstance; import com.simibubi.create.content.contraptions.relays.encased.ShaftInstance;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3f; import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.world.IBlockDisplayReader;
import net.minecraft.world.LightType; import net.minecraft.world.LightType;
public abstract class AbstractPulleyInstance extends ShaftInstance implements IDynamicInstance { public abstract class AbstractPulleyInstance extends ShaftInstance implements IDynamicInstance, IMovingListener {
final OrientedData coil; final OrientedData coil;
final SelectInstance<OrientedData> magnet; final SelectInstance<OrientedData> magnet;
@ -36,9 +32,8 @@ public abstract class AbstractPulleyInstance extends ShaftInstance implements ID
protected final Direction rotatingAbout; protected final Direction rotatingAbout;
protected final Vector3f rotationAxis; protected final Vector3f rotationAxis;
private byte[] bLight = new byte[1]; private final GridAlignedBB volume = new GridAlignedBB();
private byte[] sLight = new byte[1]; private final LightVolume light;
private GridAlignedBB volume;
public AbstractPulleyInstance(MaterialManager dispatcher, KineticTileEntity tile) { public AbstractPulleyInstance(MaterialManager dispatcher, KineticTileEntity tile) {
super(dispatcher, tile); super(dispatcher, tile);
@ -46,8 +41,7 @@ public abstract class AbstractPulleyInstance extends ShaftInstance implements ID
rotatingAbout = Direction.get(Direction.AxisDirection.POSITIVE, axis); rotatingAbout = Direction.get(Direction.AxisDirection.POSITIVE, axis);
rotationAxis = rotatingAbout.step(); rotationAxis = rotatingAbout.step();
coil = getCoilModel() coil = getCoilModel().createInstance()
.createInstance()
.setPosition(getInstancePosition()); .setPosition(getInstancePosition());
magnet = new SelectInstance<>(this::getMagnetModelIndex); magnet = new SelectInstance<>(this::getMagnetModelIndex);
@ -55,49 +49,60 @@ public abstract class AbstractPulleyInstance extends ShaftInstance implements ID
.addModel(getHalfMagnetModel()); .addModel(getHalfMagnetModel());
rope = new GroupInstance<>(getRopeModel()); rope = new GroupInstance<>(getRopeModel());
halfRope = new ConditionalInstance<>(getHalfRopeModel()) halfRope = new ConditionalInstance<>(getHalfRopeModel()).withCondition(this::shouldRenderHalfRope);
.withCondition(this::shouldRenderHalfRope);
updateOffset();
updateVolume();
light = new LightVolume(volume);
light.initialize(LightUpdater.get(world).getProvider());
beginFrame();
} }
@Override @Override
public void beginFrame() { public void beginFrame() {
updateOffset(); updateOffset();
transformModels();
}
private void transformModels() {
resizeRope();
coil.setRotation(rotationAxis.rotationDegrees(offset * 180)); coil.setRotation(rotationAxis.rotationDegrees(offset * 180));
magnet.update().get().ifPresent(data ->
{ int neededRopeCount = getNeededRopeCount();
int index = Math.max(0, MathHelper.floor(offset)); rope.resize(neededRopeCount);
magnet.update()
.get()
.ifPresent(data -> {
int i = Math.max(0, MathHelper.floor(offset));
short packed = light.getPackedLight(pos.getX(), pos.getY() - i, pos.getZ());
data.setPosition(getInstancePosition()) data.setPosition(getInstancePosition())
.nudge(0, -offset, 0) .nudge(0, -offset, 0)
.setBlockLight(bLight[index]) .setBlockLight(LightVolume.unpackBlock(packed))
.setSkyLight(sLight[index]); .setSkyLight(LightVolume.unpackSky(packed));
} });
);
halfRope.update().get().ifPresent(rope -> { halfRope.update()
float f = offset % 1; .get()
float halfRopeNudge = f > .75f ? f - 1 : f; .ifPresent(rope1 -> {
float f = offset % 1;
float halfRopeNudge = f > .75f ? f - 1 : f;
rope.setPosition(getInstancePosition()) short packed = light.getPackedLight(pos.getX(), pos.getY(), pos.getZ());
.nudge(0, -halfRopeNudge, 0) rope1.setPosition(getInstancePosition())
.setBlockLight(bLight[0]) .nudge(0, -halfRopeNudge, 0)
.setSkyLight(sLight[0]); .setBlockLight(LightVolume.unpackBlock(packed))
}); .setSkyLight(LightVolume.unpackSky(packed));
});
if (isRunning()) { if (isRunning()) {
int size = rope.size(); int size = rope.size();
int bottomY = pos.getY() - size;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
short packed = light.getPackedLight(pos.getX(), bottomY + i, pos.getZ());
rope.get(i) rope.get(i)
.setPosition(getInstancePosition()) .setPosition(getInstancePosition())
.nudge(0, -offset + i + 1, 0) .nudge(0, -offset + i + 1, 0)
.setBlockLight(bLight[size - i]) .setBlockLight(LightVolume.unpackBlock(packed))
.setSkyLight(sLight[size - i]); .setSkyLight(LightVolume.unpackSky(packed));
} }
} else { } else {
rope.clear(); rope.clear();
@ -117,6 +122,7 @@ public abstract class AbstractPulleyInstance extends ShaftInstance implements ID
magnet.delete(); magnet.delete();
rope.clear(); rope.clear();
halfRope.delete(); halfRope.delete();
light.delete();
} }
protected abstract Instancer<OrientedData> getRopeModel(); protected abstract Instancer<OrientedData> getRopeModel();
@ -133,23 +139,24 @@ public abstract class AbstractPulleyInstance extends ShaftInstance implements ID
protected abstract boolean isRunning(); protected abstract boolean isRunning();
protected void resizeRope() { @Override
int neededRopeCount = getNeededRopeCount(); public boolean update(LightProvider provider) {
rope.resize(neededRopeCount); if (updateVolume()) {
light.move(provider, volume);
int length = MathHelper.ceil(offset); return true;
if (volume == null || bLight.length < length + 1) {
volume = GridAlignedBB.from(pos.below(length), pos);
volume.fixMinMax();
bLight = Arrays.copyOf(bLight, length + 1);
sLight = Arrays.copyOf(sLight, length + 1);
initLight(BasicProvider.get(world), volume);
needsUpdate = true;
} }
return false;
}
private boolean updateVolume() {
int length = MathHelper.ceil(offset) + 2;
if (volume.sizeY() < length) {
volume.assign(pos.below(length), pos)
.fixMinMax();
return true;
}
return false;
} }
private void updateOffset() { private void updateOffset() {
@ -167,7 +174,7 @@ public abstract class AbstractPulleyInstance extends ShaftInstance implements ID
private int getMagnetModelIndex() { private int getMagnetModelIndex() {
if (isRunning() || offset == 0) { if (isRunning() || offset == 0) {
return offset > .25f ? 0 : 1; return offset > .25f ? 0 : 1;
} else { } else {
return -1; return -1;
} }
@ -178,25 +185,14 @@ public abstract class AbstractPulleyInstance extends ShaftInstance implements ID
return false; return false;
} }
boolean needsUpdate; @Override
public ImmutableBox getVolume() {
return volume;
}
@Override @Override
public void onLightUpdate(LightProvider world, LightType type, ReadOnlyBox changed) { public void onLightUpdate(LightProvider world, LightType type, ImmutableBox changed) {
initLight(world, changed.intersect(volume)); super.onLightUpdate(world, type, changed);
} light.onLightUpdate(world, type, changed);
private void initLight(LightProvider world, ReadOnlyBox changed) {
int top = this.pos.getY();
BlockPos.Mutable pos = new BlockPos.Mutable();
changed.forEachContained((x, y, z) -> {
pos.set(x, y, z);
byte block = (byte) world.getLight(LightType.BLOCK, x, y, z);
byte sky = (byte) world.getLight(LightType.SKY, x, y, z);
int i = top - y;
bLight[i] = block;
sLight[i] = sky;
});
} }
} }

View file

@ -9,11 +9,9 @@ import com.simibubi.create.content.contraptions.fluids.actors.HosePulleyTileEnti
import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.AnimationTickHolder;
public class HosePulleyInstance extends AbstractPulleyInstance { public class HosePulleyInstance extends AbstractPulleyInstance {
final HosePulleyTileEntity tile = (HosePulleyTileEntity) super.tile;
public HosePulleyInstance(MaterialManager dispatcher, HosePulleyTileEntity tile) { public HosePulleyInstance(MaterialManager dispatcher, HosePulleyTileEntity tile) {
super(dispatcher, tile); super(dispatcher, tile);
beginFrame();
} }
protected Instancer<OrientedData> getRopeModel() { protected Instancer<OrientedData> getRopeModel() {
@ -41,7 +39,7 @@ public class HosePulleyInstance extends AbstractPulleyInstance {
} }
protected float getOffset() { protected float getOffset() {
return tile.getInterpolatedOffset(AnimationTickHolder.getPartialTicks()); return ((HosePulleyTileEntity) tile).getInterpolatedOffset(AnimationTickHolder.getPartialTicks());
} }
protected boolean isRunning() { protected boolean isRunning() {

View file

@ -9,11 +9,8 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.AnimationTickHolder;
public class RopePulleyInstance extends AbstractPulleyInstance { public class RopePulleyInstance extends AbstractPulleyInstance {
final PulleyTileEntity tile = (PulleyTileEntity) super.tile;
public RopePulleyInstance(MaterialManager dispatcher, PulleyTileEntity tile) { public RopePulleyInstance(MaterialManager dispatcher, PulleyTileEntity tile) {
super(dispatcher, tile); super(dispatcher, tile);
beginFrame();
} }
protected Instancer<OrientedData> getRopeModel() { protected Instancer<OrientedData> getRopeModel() {
@ -38,10 +35,10 @@ public class RopePulleyInstance extends AbstractPulleyInstance {
protected float getOffset() { protected float getOffset() {
float partialTicks = AnimationTickHolder.getPartialTicks(); float partialTicks = AnimationTickHolder.getPartialTicks();
return PulleyRenderer.getTileOffset(partialTicks, tile); return PulleyRenderer.getTileOffset(partialTicks, (PulleyTileEntity) tile);
} }
protected boolean isRunning() { protected boolean isRunning() {
return tile.running || tile.isVirtual(); return ((PulleyTileEntity) tile).running || tile.isVirtual();
} }
} }

View file

@ -1,35 +0,0 @@
package com.simibubi.create.content.contraptions.components.structureMovement.render;
import java.util.ArrayList;
import com.jozufozu.flywheel.light.GridAlignedBB;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
public class LightVolumeDebugger {
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {
// ContraptionRenderDispatcher.RENDERERS.values()
// .stream()
// .flatMap(r -> {
// GridAlignedBB texture = r.getLighter().lightVolume.getTextureVolume();
// GridAlignedBB sample = r.getLighter().lightVolume.getSampleVolume();
//
// ArrayList<Pair<GridAlignedBB, Integer>> pairs = new ArrayList<>(2);
//
// pairs.add(Pair.of(texture, 0xFFFFFF));
// pairs.add(Pair.of(sample, 0xFFFF00));
//
// return pairs.stream();
// })
// .map(pair -> {
// AABBOutline outline = new AABBOutline(GridAlignedBB.toAABB(pair.getFirst()));
//
// outline.getParams().colored(pair.getSecond());
// return outline;
// })
// .forEach(outline -> outline.render(ms, buffer, AnimationTickHolder.getPartialTicks()));
}
}

View file

@ -14,7 +14,6 @@ import com.jozufozu.flywheel.backend.model.ModelRenderer;
import com.jozufozu.flywheel.core.model.IModel; import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.core.model.WorldModel; import com.jozufozu.flywheel.core.model.WorldModel;
import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.light.GridAlignedBB;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
@ -85,7 +84,7 @@ public class RenderedContraption extends ContraptionRenderInfo {
Vector3d cameraPos = event.getCameraPos(); Vector3d cameraPos = event.getCameraPos();
lightBox = lighter.lightVolume.getTextureVolume().toAABB() lightBox = lighter.lightVolume.toAABB()
.move(-cameraPos.x, -cameraPos.y, -cameraPos.z); .move(-cameraPos.x, -cameraPos.y, -cameraPos.z);
} }
@ -111,7 +110,7 @@ public class RenderedContraption extends ContraptionRenderInfo {
} }
renderLayers.clear(); renderLayers.clear();
lighter.lightVolume.delete(); lighter.delete();
materialManager.delete(); materialManager.delete();
kinetics.invalidate(); kinetics.invalidate();

View file

@ -18,7 +18,7 @@ import com.jozufozu.flywheel.light.ILightUpdateListener;
import com.jozufozu.flywheel.light.LightProvider; import com.jozufozu.flywheel.light.LightProvider;
import com.jozufozu.flywheel.light.LightUpdater; import com.jozufozu.flywheel.light.LightUpdater;
import com.jozufozu.flywheel.light.ListenerStatus; import com.jozufozu.flywheel.light.ListenerStatus;
import com.jozufozu.flywheel.light.ReadOnlyBox; import com.jozufozu.flywheel.light.ImmutableBox;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.IRotate;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
@ -553,7 +553,7 @@ public class BeltTileEntity extends KineticTileEntity implements ILightUpdateLis
} }
@Override @Override
public void onLightUpdate(LightProvider world, LightType type, ReadOnlyBox changed) { public void onLightUpdate(LightProvider world, LightType type, ImmutableBox changed) {
GridAlignedBB beltVolume = getVolume(); GridAlignedBB beltVolume = getVolume();
if (beltVolume.intersects(changed)) { if (beltVolume.intersects(changed)) {