diff --git a/src/main/java/com/simibubi/create/content/contraptions/render/ContraptionVisual.java b/src/main/java/com/simibubi/create/content/contraptions/render/ContraptionVisual.java index 4ce2d24e8..b82288e3d 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/render/ContraptionVisual.java +++ b/src/main/java/com/simibubi/create/content/contraptions/render/ContraptionVisual.java @@ -47,15 +47,19 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp import net.minecraftforge.client.model.data.ModelData; public class ContraptionVisual extends AbstractEntityVisual implements DynamicVisual, TickableVisual, LitVisual { + protected static final int LIGHT_PADDING = 1; + protected final VisualEmbedding embedding; - private final List> children = new ArrayList<>(); - private final List actors = new ArrayList<>(); - private final PlanMap dynamicVisuals = new PlanMap<>(); - private final PlanMap tickableVisuals = new PlanMap<>(); - private VirtualRenderWorld virtualRenderWorld; - private Notifier notifier; - private Model model; - private TransformedInstance structure; + protected final List> children = new ArrayList<>(); + protected final List actors = new ArrayList<>(); + protected final PlanMap dynamicVisuals = new PlanMap<>(); + protected final PlanMap tickableVisuals = new PlanMap<>(); + protected VirtualRenderWorld virtualRenderWorld; + protected Model model; + protected TransformedInstance structure; + protected Notifier notifier; + protected long minSection, maxSection; + protected long minBlock, maxBlock; private final PoseStack contraptionMatrix = new PoseStack(); @@ -171,7 +175,16 @@ public class ContraptionVisual extends Abst var partialTick = context.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) { @@ -188,42 +201,73 @@ public class ContraptionVisual extends Abst @Override public void updateLight() { + embedding.invalidateLight(); // FIXME: Some blocks (e.g. large waterwheels) extend well beyond their actual block // and might have lighting issues here var boundingBox = entity.getBoundingBox(); - int minX = Mth.floor(boundingBox.minX) - 1; - int minY = Mth.floor(boundingBox.minY) - 1; - int minZ = Mth.floor(boundingBox.minZ) - 1; - int sizeX = Mth.ceil(boundingBox.maxX) - minX + 2; - int sizeY = Mth.ceil(boundingBox.maxY) - minY + 2; - int sizeZ = Mth.ceil(boundingBox.maxZ) - minZ + 2; + 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); - 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 public void collectLightSections(LongConsumer consumer) { var boundingBox = entity.getBoundingBox(); - int minX = Mth.floor(boundingBox.minX) - 1; - int minY = Mth.floor(boundingBox.minY) - 1; - int minZ = Mth.floor(boundingBox.minZ) - 1; - int sizeXChunks = SectionPos.blockToSectionCoord(Mth.ceil(boundingBox.maxX) - minX + 2) + 1; - int sizeYChunks = SectionPos.blockToSectionCoord(Mth.ceil(boundingBox.maxY) - minY + 2) + 1; - int sizeZChunks = SectionPos.blockToSectionCoord(Mth.ceil(boundingBox.maxZ) - minZ + 2) + 1; + 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); - 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 y = 0; y < sizeYChunks; y++) { - for (int z = 0; z < sizeZChunks; z++) { - consumer.accept(SectionPos.offset(base, x, y, z)); + for (int x = 0; x <= maxSectionX - minSectionX; x++) { + for (int y = 0; y <= maxSectionY - minSectionY; y++) { + for (int z = 0; z <= maxSectionZ - minSectionZ; 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 public void initLightSectionNotifier(Notifier notifier) { this.notifier = notifier; @@ -243,4 +287,20 @@ public class ContraptionVisual extends Abst 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)); + } }