Light work

- Track the min/max sections contraptions reported themselves in
- When the current min/max sections don't match, invoke the notifier
- Track the min/max block positions contraptions collected light from
- When the current min/max positions don't match, re-collect all light
- Invalidate light before collecting
This commit is contained in:
Jozufozu 2024-03-22 08:49:04 -07:00
parent 99b886ce15
commit 42b1e19fc8

View file

@ -47,15 +47,19 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
import net.minecraftforge.client.model.data.ModelData; import net.minecraftforge.client.model.data.ModelData;
public class ContraptionVisual<E extends AbstractContraptionEntity> extends AbstractEntityVisual<E> implements DynamicVisual, TickableVisual, LitVisual { public class ContraptionVisual<E extends AbstractContraptionEntity> extends AbstractEntityVisual<E> implements DynamicVisual, TickableVisual, LitVisual {
protected static final int LIGHT_PADDING = 1;
protected final VisualEmbedding embedding; protected final VisualEmbedding embedding;
private final List<BlockEntityVisual<?>> children = new ArrayList<>(); protected final List<BlockEntityVisual<?>> children = new ArrayList<>();
private final List<ActorVisual> actors = new ArrayList<>(); protected final List<ActorVisual> actors = new ArrayList<>();
private final PlanMap<DynamicVisual, VisualFrameContext> dynamicVisuals = new PlanMap<>(); protected final PlanMap<DynamicVisual, VisualFrameContext> dynamicVisuals = new PlanMap<>();
private final PlanMap<TickableVisual, VisualTickContext> tickableVisuals = new PlanMap<>(); protected final PlanMap<TickableVisual, VisualTickContext> tickableVisuals = new PlanMap<>();
private VirtualRenderWorld virtualRenderWorld; protected VirtualRenderWorld virtualRenderWorld;
private Notifier notifier; protected Model model;
private Model model; protected TransformedInstance structure;
private TransformedInstance structure; protected Notifier notifier;
protected long minSection, maxSection;
protected long minBlock, maxBlock;
private final PoseStack contraptionMatrix = new PoseStack(); private final PoseStack contraptionMatrix = new PoseStack();
@ -171,7 +175,16 @@ public class ContraptionVisual<E extends AbstractContraptionEntity> extends Abst
var partialTick = context.partialTick(); var partialTick = context.partialTick();
setEmbeddingMatrices(partialTick); setEmbeddingMatrices(partialTick);
// TODO: re-collect light if needed if (hasMovedSections()) {
notifier.notifySectionsChanged();
}
if (hasMovedBlocks()) {
// TODO: incremental light collection
// TODO: optimize light collection for very large contraptions
// by only collecting cuboids that contain faces
updateLight();
}
} }
private void setEmbeddingMatrices(float partialTick) { private void setEmbeddingMatrices(float partialTick) {
@ -188,42 +201,73 @@ public class ContraptionVisual<E extends AbstractContraptionEntity> extends Abst
@Override @Override
public void updateLight() { public void updateLight() {
embedding.invalidateLight();
// FIXME: Some blocks (e.g. large waterwheels) extend well beyond their actual block // FIXME: Some blocks (e.g. large waterwheels) extend well beyond their actual block
// and might have lighting issues here // and might have lighting issues here
var boundingBox = entity.getBoundingBox(); var boundingBox = entity.getBoundingBox();
int minX = Mth.floor(boundingBox.minX) - 1; int minX = minLight(boundingBox.minX);
int minY = Mth.floor(boundingBox.minY) - 1; int minY = minLight(boundingBox.minY);
int minZ = Mth.floor(boundingBox.minZ) - 1; int minZ = minLight(boundingBox.minZ);
int sizeX = Mth.ceil(boundingBox.maxX) - minX + 2; int maxX = maxLight(boundingBox.maxX);
int sizeY = Mth.ceil(boundingBox.maxY) - minY + 2; int maxY = maxLight(boundingBox.maxY);
int sizeZ = Mth.ceil(boundingBox.maxZ) - minZ + 2; int maxZ = maxLight(boundingBox.maxZ);
embedding.collectLight(level, minX, minY, minZ, sizeX, sizeY, sizeZ); minBlock = BlockPos.asLong(minX, minY, minZ);
maxBlock = BlockPos.asLong(maxX, maxY, maxZ);
embedding.collectLight(level, minX, minY, minZ, maxX - minX, maxY - minY, maxZ - minZ);
} }
@Override @Override
public void collectLightSections(LongConsumer consumer) { public void collectLightSections(LongConsumer consumer) {
var boundingBox = entity.getBoundingBox(); var boundingBox = entity.getBoundingBox();
int minX = Mth.floor(boundingBox.minX) - 1; var minSectionX = minLightSection(boundingBox.minX);
int minY = Mth.floor(boundingBox.minY) - 1; var minSectionY = minLightSection(boundingBox.minY);
int minZ = Mth.floor(boundingBox.minZ) - 1; var minSectionZ = minLightSection(boundingBox.minZ);
int sizeXChunks = SectionPos.blockToSectionCoord(Mth.ceil(boundingBox.maxX) - minX + 2) + 1; int maxSectionX = maxLightSection(boundingBox.maxX);
int sizeYChunks = SectionPos.blockToSectionCoord(Mth.ceil(boundingBox.maxY) - minY + 2) + 1; int maxSectionY = maxLightSection(boundingBox.maxY);
int sizeZChunks = SectionPos.blockToSectionCoord(Mth.ceil(boundingBox.maxZ) - minZ + 2) + 1; int maxSectionZ = maxLightSection(boundingBox.maxZ);
var base = SectionPos.asLong(SectionPos.blockToSectionCoord(minX), SectionPos.blockToSectionCoord(minY), SectionPos.blockToSectionCoord(minZ)); minSection = SectionPos.asLong(minSectionX, minSectionY, minSectionZ);
maxSection = SectionPos.asLong(maxSectionX, maxSectionY, maxSectionZ);
for (int x = 0; x < sizeXChunks; x++) { for (int x = 0; x <= maxSectionX - minSectionX; x++) {
for (int y = 0; y < sizeYChunks; y++) { for (int y = 0; y <= maxSectionY - minSectionY; y++) {
for (int z = 0; z < sizeZChunks; z++) { for (int z = 0; z <= maxSectionZ - minSectionZ; z++) {
consumer.accept(SectionPos.offset(base, x, y, z)); consumer.accept(SectionPos.offset(minSection, x, y, z));
} }
} }
} }
} }
protected boolean hasMovedBlocks() {
var boundingBox = entity.getBoundingBox();
int minX = minLight(boundingBox.minX);
int minY = minLight(boundingBox.minY);
int minZ = minLight(boundingBox.minZ);
int maxX = maxLight(boundingBox.maxX);
int maxY = maxLight(boundingBox.maxY);
int maxZ = maxLight(boundingBox.maxZ);
return minBlock != BlockPos.asLong(minX, minY, minZ) || maxBlock != BlockPos.asLong(maxX, maxY, maxZ);
}
protected boolean hasMovedSections() {
var boundingBox = entity.getBoundingBox();
var minSectionX = minLightSection(boundingBox.minX);
var minSectionY = minLightSection(boundingBox.minY);
var minSectionZ = minLightSection(boundingBox.minZ);
int maxSectionX = maxLightSection(boundingBox.maxX);
int maxSectionY = maxLightSection(boundingBox.maxY);
int maxSectionZ = maxLightSection(boundingBox.maxZ);
return minSection != SectionPos.asLong(minSectionX, minSectionY, minSectionZ) || maxSection != SectionPos.asLong(maxSectionX, maxSectionY, maxSectionZ);
}
@Override @Override
public void initLightSectionNotifier(Notifier notifier) { public void initLightSectionNotifier(Notifier notifier) {
this.notifier = notifier; this.notifier = notifier;
@ -243,4 +287,20 @@ public class ContraptionVisual<E extends AbstractContraptionEntity> extends Abst
structure.delete(); structure.delete();
} }
} }
public static int minLight(double aabbPos) {
return Mth.floor(aabbPos) - LIGHT_PADDING;
}
public static int maxLight(double aabbPos) {
return Mth.ceil(aabbPos) + LIGHT_PADDING;
}
public static int minLightSection(double aabbPos) {
return SectionPos.blockToSectionCoord(minLight(aabbPos));
}
public static int maxLightSection(double aabbPos) {
return SectionPos.blockToSectionCoord(maxLight(aabbPos));
}
} }