From 5140409c71709ff5d1190ada3ac2d7c88f5e1e49 Mon Sep 17 00:00:00 2001 From: ryanhcode <42245712+ryanhcode@users.noreply.github.com> Date: Sun, 5 Oct 2025 03:24:54 -0400 Subject: [PATCH] Necessary changes for sable compatibility --- .../api/visualization/VisualEmbedding.java | 6 + .../backend/engine/LightDataCollector.java | 14 +- .../flywheel/backend/engine/LightLut.java | 27 ++- .../flywheel/backend/engine/LightStorage.java | 181 ++++++++++-------- .../engine/embed/EmbeddedEnvironment.java | 38 +++- .../engine/embed/EmbeddingUniforms.java | 3 + .../engine/embed/EnvironmentStorage.java | 16 +- .../backend/engine/indirect/MatrixBuffer.java | 2 +- .../flywheel/flywheel/internal/api_impl.glsl | 4 +- .../flywheel/flywheel/internal/common.vert | 9 + .../flywheel/internal/indirect/main.vert | 2 +- .../flywheel/internal/indirect/matrices.glsl | 10 +- .../flywheel/internal/instancing/main.vert | 6 + .../flywheel/flywheel/internal/light_lut.glsl | 19 +- .../assets/flywheel/flywheel/light/flat.glsl | 30 ++- .../flywheel/flywheel/light/smooth.glsl | 29 ++- .../flywheel/light/smooth_when_embedded.glsl | 16 +- docs/flywheel/api/stage/common.glsl | 4 +- gradle.properties | 4 +- 19 files changed, 300 insertions(+), 120 deletions(-) diff --git a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualEmbedding.java b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualEmbedding.java index b4f626590..748ff9fbb 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualEmbedding.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/visualization/VisualEmbedding.java @@ -1,6 +1,7 @@ package dev.engine_room.flywheel.api.visualization; import org.joml.Matrix3fc; +import org.joml.Matrix4f; import org.joml.Matrix4fc; import dev.engine_room.flywheel.api.backend.BackendImplemented; @@ -24,6 +25,11 @@ public interface VisualEmbedding extends VisualizationContext { */ void transforms(Matrix4fc pose, Matrix3fc normal); + /** + * Set the scene ID used for lighting in this embedding + */ + void setLightingInfo(Matrix4fc sceneMatrix, int scene, float skyLightScale); + /** * Delete this embedding. * diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightDataCollector.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightDataCollector.java index 1639ee7cc..49929c738 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightDataCollector.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightDataCollector.java @@ -91,12 +91,12 @@ private static Long2ObjectFunction createFastBlockDataGetter(LayerLig return null; } - public void collectSection(long ptr, long section) { - collectSolidData(ptr, section); - collectLightData(ptr, section); + public void collectSection(long ptr, int scene, long section) { + collectSolidData(ptr, scene, section); + collectLightData(ptr, scene, section); } - private void collectSolidData(long ptr, long section) { + private void collectSolidData(long ptr, int scene, long section) { var blockPos = new BlockPos.MutableBlockPos(); int xMin = SectionPos.sectionToBlockCoord(SectionPos.x(section)); int yMin = SectionPos.sectionToBlockCoord(SectionPos.y(section)); @@ -127,7 +127,7 @@ private void collectSolidData(long ptr, long section) { } } - protected abstract void collectLightData(long ptr, long section); + protected abstract void collectLightData(long ptr, int scene, long section); /** * Write to the given section. @@ -162,7 +162,7 @@ public Fast(LevelAccessor level, LayerLightEventListener skyLayerListener, Layer } @Override - protected void collectLightData(long ptr, long section) { + protected void collectLightData(long ptr, int scene, long section) { collectCenter(ptr, section); for (SectionEdge i : SectionEdge.VALUES) { @@ -299,7 +299,7 @@ public Slow(LevelAccessor level, LayerLightEventListener skyLayerListener, Layer } @Override - protected void collectLightData(long ptr, long section) { + protected void collectLightData(long ptr, int scene, long section) { var blockLayerListener = this.blockLayerListener; var skyLayerListener = this.skyLayerListener; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java index 987ae674b..7494bc666 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java @@ -11,46 +11,55 @@ // Massive kudos to RogueLogix for figuring out this LUT scheme. // First layer is Y, then X, then Z. public final class LightLut { - public final Layer> indices = new Layer<>(); + public final Layer>> indices = new Layer<>(); - public void add(long position, int index) { + public void add(int scene, long position, int index) { final var x = SectionPos.x(position); final var y = SectionPos.y(position); final var z = SectionPos.z(position); - indices.computeIfAbsent(y, Layer::new) + indices.computeIfAbsent(scene, Layer::new) + .computeIfAbsent(y, Layer::new) .computeIfAbsent(x, IntLayer::new) .set(z, index + 1); } public void prune() { // Maybe this could be done better incrementally? - indices.prune((middle) -> middle.prune(IntLayer::prune)); + indices.prune((scene) -> scene.prune((middle) -> middle.prune(IntLayer::prune))); } - public void remove(long section) { + public void remove(int scene, long section) { final var x = SectionPos.x(section); final var y = SectionPos.y(section); final var z = SectionPos.z(section); - var first = indices.get(y); + var first = indices.get(scene); if (first == null) { return; } - var second = first.get(x); + var second = first.get(y); if (second == null) { return; } - second.clear(z); + var third = second.get(x); + + if (third == null) { + return; + } + + third.clear(z); } public IntArrayList flatten() { final var out = new IntArrayList(); - indices.fillLut(out, (yIndices, lut) -> yIndices.fillLut(lut, IntLayer::fillLut)); + this.indices.fillLut(out, (sceneIndices, lut1) -> sceneIndices.fillLut(lut1, + (yIndices, lut2) -> yIndices.fillLut(lut2, IntLayer::fillLut) + )); return out; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java index 8462cdb9b..476d83086 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java @@ -2,8 +2,9 @@ import java.util.BitSet; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; + import org.jetbrains.annotations.Nullable; -import org.lwjgl.system.MemoryUtil; import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.visual.Effect; @@ -20,9 +21,10 @@ import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual; import dev.engine_room.flywheel.lib.visual.component.HitboxComponent; import dev.engine_room.flywheel.lib.visual.util.InstanceRecycler; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.longs.Long2IntMap; -import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.client.renderer.LightTexture; @@ -30,6 +32,8 @@ import net.minecraft.core.Vec3i; import net.minecraft.world.level.LevelAccessor; +import org.lwjgl.system.MemoryUtil; + /** * A managed arena of light sections for uploading to the GPU. * @@ -52,17 +56,18 @@ public class LightStorage implements Effect { private static final int DEFAULT_ARENA_CAPACITY_SECTIONS = 64; private static final int INVALID_SECTION = -1; + public static final int STATIC_SCENE_ID = 0; + private final LevelAccessor level; private final LightLut lut; public final CpuArena arena; - private final Long2IntMap section2ArenaIndex; + private final Int2ObjectMap scene2SectionArenaIndexMap; private final LightDataCollector collector; private final BitSet changed = new BitSet(); + private final LongSet updatedSections = new LongOpenHashSet(); private boolean needsLutRebuild = false; private boolean isDebugOn = false; - - private final LongSet updatedSections = new LongOpenHashSet(); @Nullable private LongSet requestedSections; @@ -70,8 +75,7 @@ public LightStorage(LevelAccessor level) { this.level = level; lut = new LightLut(); arena = new CpuArena(SECTION_SIZE_BYTES, DEFAULT_ARENA_CAPACITY_SECTIONS); - section2ArenaIndex = new Long2IntOpenHashMap(); - section2ArenaIndex.defaultReturnValue(INVALID_SECTION); + scene2SectionArenaIndexMap = new Int2ObjectOpenHashMap<>(); collector = LightDataCollector.of(level); } @@ -121,42 +125,82 @@ public Plan createFramePlan() { return; } - removeUnusedSections(); - - // Start building the set of sections we need to collect this frame. - LongSet sectionsToCollect; - if (requestedSections == null) { - // If none were requested, then we need to collect all sections that received updates. - sectionsToCollect = new LongOpenHashSet(); - } else { - // If we did receive a new set of requested sections, we only - // need to collect the sections that weren't yet tracked. - sectionsToCollect = new LongOpenHashSet(requestedSections); + updateLightSections(); + }); + } + + private void updateLightSections() { + removeUnusedSections(); + + int scene = STATIC_SCENE_ID; + + // Start building the set of sections we need to collect this frame. + LongSet sectionsToCollect; + Long2IntMap section2ArenaIndex = scene2SectionArenaIndexMap.get(scene); + if (requestedSections == null) { + // If none were requested, then we need to collect all sections that received updates. + sectionsToCollect = new LongOpenHashSet(); + } else { + // If we did receive a new set of requested sections, we only + // need to collect the sections that weren't yet tracked. + sectionsToCollect = new LongOpenHashSet(requestedSections); + + if (section2ArenaIndex != null) { sectionsToCollect.removeAll(section2ArenaIndex.keySet()); } + } - // updatedSections contains all sections that received light updates, - // but we only care about its intersection with our tracked sections. - for (long updatedSection : updatedSections) { - // Since sections contain the border light of their neighbors, we need to collect the neighbors as well. - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - for (int z = -1; z <= 1; z++) { - long section = SectionPos.offset(updatedSection, x, y, z); - if (section2ArenaIndex.containsKey(section)) { - sectionsToCollect.add(section); - } + // updatedSections contains all sections that received light updates, + // but we only care about its intersection with our tracked sections. + for (long updatedSection : updatedSections) { + // Since sections contain the border light of their neighbors, we need to collect the neighbors as well. + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + long section = SectionPos.offset(updatedSection, x, y, z); + if (section2ArenaIndex != null && section2ArenaIndex.containsKey(section)) { + sectionsToCollect.add(section); } } } } + } - // Now actually do the collection. - sectionsToCollect.forEach(this::collectSection); + // Now actually do the collection. + sectionsToCollect.forEach(section -> this.collectSection(scene, section)); - updatedSections.clear(); - requestedSections = null; - }); + updatedSections.clear(); + requestedSections = null; + } + + public void collectSection(int scene, long section) { + int index = indexForSection(scene, section); + + changed.set(index); + + long ptr = arena.indexToPointer(index); + + // Zero it out first. This is basically free and makes it easier to handle missing sections later. + MemoryUtil.memSet(ptr, 0, SECTION_SIZE_BYTES); + + collector.collectSection(ptr, scene, section); + } + + private int indexForSection(int scene, long section) { + Long2IntMap map = this.scene2SectionArenaIndexMap.get(scene); + int out = map != null ? map.get(section) : INVALID_SECTION; + + // Need to allocate. + if (out == INVALID_SECTION) { + out = arena.alloc(); + this.scene2SectionArenaIndexMap.computeIfAbsent(scene, (ignored) -> { + Long2IntOpenHashMap newMap = new Long2IntOpenHashMap(); + newMap.defaultReturnValue(INVALID_SECTION); + return newMap; + }).put(section, out); + beginTrackingSection(scene, section, out); + } + return out; } private void removeUnusedSections() { @@ -166,17 +210,22 @@ private void removeUnusedSections() { boolean anyRemoved = false; - var entries = section2ArenaIndex.long2IntEntrySet(); - var it = entries.iterator(); - while (it.hasNext()) { - var entry = it.next(); - var section = entry.getLongKey(); - - if (!requestedSections.contains(section)) { - arena.free(entry.getIntValue()); - endTrackingSection(section); - it.remove(); - anyRemoved = true; + for (Int2ObjectMap.Entry sceneEntry : this.scene2SectionArenaIndexMap.int2ObjectEntrySet()) { + int sceneId = sceneEntry.getIntKey(); + Long2IntMap section2ArenaIndex = sceneEntry.getValue(); + + var entries = section2ArenaIndex.long2IntEntrySet(); + var it = entries.iterator(); + while (it.hasNext()) { + var entry = it.next(); + var section = entry.getLongKey(); + + if (!requestedSections.contains(section)) { + arena.free(entry.getIntValue()); + endTrackingSection(sceneId, section); + it.remove(); + anyRemoved = true; + } } } @@ -186,13 +235,13 @@ private void removeUnusedSections() { } } - private void beginTrackingSection(long section, int index) { - lut.add(section, index); + private void beginTrackingSection(int scene, long section, int index) { + lut.add(scene, section, index); needsLutRebuild = true; } - private void endTrackingSection(long section) { - lut.remove(section); + private void endTrackingSection(int scene, long section) { + lut.remove(scene, section); needsLutRebuild = true; } @@ -200,31 +249,6 @@ public int capacity() { return arena.capacity(); } - public void collectSection(long section) { - int index = indexForSection(section); - - changed.set(index); - - long ptr = arena.indexToPointer(index); - - // Zero it out first. This is basically free and makes it easier to handle missing sections later. - MemoryUtil.memSet(ptr, 0, SECTION_SIZE_BYTES); - - collector.collectSection(ptr, section); - } - - private int indexForSection(long section) { - int out = section2ArenaIndex.get(section); - - // Need to allocate. - if (out == INVALID_SECTION) { - out = arena.alloc(); - section2ArenaIndex.put(section, out); - beginTrackingSection(section, out); - } - return out; - } - public void delete() { arena.delete(); } @@ -278,7 +302,10 @@ public void beginFrame(Context ctx) { } private void setupSectionBoxes() { - section2ArenaIndex.keySet() + for (Int2ObjectMap.Entry entry : scene2SectionArenaIndexMap.int2ObjectEntrySet()) { + int sceneId = entry.getIntKey(); + Long2IntMap section2ArenaIndex = entry.getValue(); + section2ArenaIndex.keySet() .forEach(l -> { var x = SectionPos.x(l) * 16 - renderOrigin.getX(); var y = SectionPos.y(l) * 16 - renderOrigin.getY(); @@ -291,10 +318,12 @@ private void setupSectionBoxes() { instance.setIdentityTransform() .translate(x + 1, y + 1, z + 1) .scale(14) - .color(255, 255, 0) + .color(255, 255, sceneId * 64) .light(LightTexture.FULL_BRIGHT) .setChanged(); }); + } + } private void setupLutRangeBoxes() { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java index e45dceec0..08672c7ef 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java @@ -1,5 +1,7 @@ package dev.engine_room.flywheel.backend.engine.embed; +import dev.engine_room.flywheel.backend.engine.LightStorage; + import org.jetbrains.annotations.Nullable; import org.joml.Matrix3f; import org.joml.Matrix3fc; @@ -18,6 +20,8 @@ import dev.engine_room.flywheel.lib.util.ExtraMemoryOps; import net.minecraft.core.Vec3i; +import org.lwjgl.system.MemoryUtil; + public class EmbeddedEnvironment implements VisualEmbedding, Environment { private final EngineImpl engine; private final Vec3i renderOrigin; @@ -27,10 +31,12 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment { private final Matrix4f pose = new Matrix4f(); private final Matrix3f normal = new Matrix3f(); + private final Matrix4f scene = new Matrix4f(); private final Matrix4f poseComposed = new Matrix4f(); private final Matrix3f normalComposed = new Matrix3f(); - - public int matrixIndex = 0; + private int sceneId = LightStorage.STATIC_SCENE_ID; + private float skyLightScale = 1.0f; + public int infoIndex = 0; private boolean deleted = false; @@ -58,6 +64,12 @@ public void transforms(Matrix4fc pose, Matrix3fc normal) { this.normal.set(normal); } + public void setLightingInfo(Matrix4fc sceneMatrix, int scene, float skyLightScale) { + this.scene.set(sceneMatrix); + this.sceneId = scene; + this.skyLightScale = skyLightScale; + } + @Override public InstancerProvider instancerProvider() { return instancerProvider; @@ -85,11 +97,19 @@ public ContextShader contextShader() { public void setupDraw(GlProgram program) { program.setMat4(EmbeddingUniforms.MODEL_MATRIX, poseComposed); program.setMat3(EmbeddingUniforms.NORMAL_MATRIX, normalComposed); + program.setUInt(EmbeddingUniforms.SCENE, sceneId); + program.setFloat(EmbeddingUniforms.SKY_LIGHT_SCALE, skyLightScale); + + if (sceneId == 0) { + program.setMat4(EmbeddingUniforms.SCENE_MATRIX, poseComposed); + } else { + program.setMat4(EmbeddingUniforms.SCENE_MATRIX, scene); + } } @Override public int matrixIndex() { - return matrixIndex; + return infoIndex; } public void flush(long ptr) { @@ -100,6 +120,18 @@ public void flush(long ptr) { ExtraMemoryOps.putMatrix4f(ptr, poseComposed); ExtraMemoryOps.putMatrix3fPadded(ptr + 16 * Float.BYTES, normalComposed); + + MemoryUtil.memPutFloat(ptr + 28 * Float.BYTES, skyLightScale); + MemoryUtil.memPutInt(ptr + 29 * Float.BYTES, sceneId); + MemoryUtil.memPutFloat(ptr + 30 * Float.BYTES, 0); + MemoryUtil.memPutFloat(ptr + 31 * Float.BYTES, 0); + + final long sceneMatrixOffset = ptr + 32 * Float.BYTES; + if (sceneId == 0) { + ExtraMemoryOps.putMatrix4f(sceneMatrixOffset, poseComposed); + } else { + ExtraMemoryOps.putMatrix4f(sceneMatrixOffset, scene); + } } private void composeMatrices(Matrix4f pose, Matrix3f normal) { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddingUniforms.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddingUniforms.java index e1c4c1978..7742fed27 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddingUniforms.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddingUniforms.java @@ -3,6 +3,9 @@ public final class EmbeddingUniforms { public static final String MODEL_MATRIX = "_flw_modelMatrixUniform"; public static final String NORMAL_MATRIX = "_flw_normalMatrixUniform"; + public static final String SCENE_MATRIX = "_flw_lightingSceneMatrixUniform"; + public static final String SCENE = "_flw_lightingSceneUniform"; + public static final String SKY_LIGHT_SCALE = "_flw_lightingSkyLightScaleUniform"; private EmbeddingUniforms() { } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EnvironmentStorage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EnvironmentStorage.java index 85ad55387..7deb97b2c 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EnvironmentStorage.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EnvironmentStorage.java @@ -5,7 +5,11 @@ import it.unimi.dsi.fastutil.objects.ReferenceSet; public class EnvironmentStorage { - public static final int MATRIX_SIZE_BYTES = (16 + 12) * Float.BYTES; + public static final int INFO_SIZE_BYTES = (16 + 12) * Float.BYTES + + Float.BYTES + // sky light scale + Integer.BYTES + // scene ID + 2 * Float.BYTES + // padding + 16 * Float.BYTES; // scene matrix protected final Object lock = new Object(); @@ -13,7 +17,7 @@ public class EnvironmentStorage { // Note than the arena starts indexing at zero, but we reserve zero for the identity matrix. // Any time an ID from the arena is written we want to add one to it. - public final CpuArena arena = new CpuArena(MATRIX_SIZE_BYTES, 32); + public final CpuArena arena = new CpuArena(INFO_SIZE_BYTES, 32); { // Reserve the identity matrix. Burns a few bytes but oh well. @@ -23,7 +27,7 @@ public class EnvironmentStorage { public void track(EmbeddedEnvironment environment) { synchronized (lock) { if (environments.add(environment)) { - environment.matrixIndex = arena.alloc(); + environment.infoIndex = arena.alloc(); } } } @@ -31,13 +35,13 @@ public void track(EmbeddedEnvironment environment) { public void flush() { environments.removeIf(embeddedEnvironment -> { var deleted = embeddedEnvironment.isDeleted(); - if (deleted && embeddedEnvironment.matrixIndex > 0) { - arena.free(embeddedEnvironment.matrixIndex); + if (deleted && embeddedEnvironment.infoIndex > 0) { + arena.free(embeddedEnvironment.infoIndex); } return deleted; }); for (EmbeddedEnvironment environment : environments) { - environment.flush(arena.indexToPointer(environment.matrixIndex)); + environment.flush(arena.indexToPointer(environment.infoIndex)); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MatrixBuffer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MatrixBuffer.java index 7328bcb3b..5eaba51e0 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MatrixBuffer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/MatrixBuffer.java @@ -6,7 +6,7 @@ import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage; public class MatrixBuffer { - private final ResizableStorageArray matrices = new ResizableStorageArray(EnvironmentStorage.MATRIX_SIZE_BYTES); + private final ResizableStorageArray matrices = new ResizableStorageArray(EnvironmentStorage.INFO_SIZE_BYTES); public void flush(StagingBuffer stagingBuffer, EnvironmentStorage environmentStorage) { var arena = environmentStorage.arena; diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/api_impl.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/api_impl.glsl index 1738c38e6..f31ae1c2e 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/api_impl.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/api_impl.glsl @@ -5,8 +5,8 @@ struct FlwLightAo { /// Get the light at the given world position relative to flw_renderOrigin from the given normal. /// This may be interpolated for smooth lighting. -bool flw_light(vec3 worldPos, vec3 normal, out FlwLightAo light); +bool flw_light(uint scene, vec3 worldPos, vec3 normal, ivec3 renderOrigin, out FlwLightAo light); /// Fetches the light value at the given block position. /// Returns false if the light for the given block is not available. -bool flw_lightFetch(ivec3 blockPos, out vec2 light); +bool flw_lightFetch(uint scene, ivec3 blockPos, out vec2 light); diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.vert b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.vert index 3c2ffecdc..022b6db54 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/common.vert +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/common.vert @@ -69,6 +69,12 @@ vec2 getCrumblingTexCoord() { #ifdef FLW_EMBEDDED mat4 _flw_modelMatrix; mat3 _flw_normalMatrix; +uint _flw_lightingSceneId; +float _flw_skyLightScale; +mat4 _flw_lightingSceneMatrix; +flat out uint flw_vertexLightingSceneId; +flat out float flw_skyLightScale; +out vec4 flw_vertexLightingPos; #endif #ifdef _FLW_DEBUG @@ -87,8 +93,11 @@ void _flw_main(in FlwInstance instance, in uint stableInstanceID, in uint baseVe #endif #ifdef FLW_EMBEDDED + flw_vertexLightingPos = _flw_lightingSceneMatrix * flw_vertexPos; flw_vertexPos = _flw_modelMatrix * flw_vertexPos; flw_vertexNormal = _flw_normalMatrix * flw_vertexNormal; + flw_vertexLightingSceneId = _flw_lightingSceneId; + flw_skyLightScale = _flw_skyLightScale; #endif flw_vertexNormal = normalize(flw_vertexNormal); diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/main.vert b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/main.vert index 7058c21de..ee3a3710e 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/main.vert +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/main.vert @@ -40,7 +40,7 @@ void main() { _flw_packedMaterial = uvec2(draw.packedFogAndCutout, packedMaterialProperties); #ifdef FLW_EMBEDDED - _flw_unpackMatrices(_flw_matrices[draw.matrixIndex], _flw_modelMatrix, _flw_normalMatrix); + _flw_unpackMatrices(_flw_matrices[draw.matrixIndex], _flw_modelMatrix, _flw_normalMatrix, _flw_lightingSceneId, _flw_skyLightScale, _flw_lightingSceneMatrix); #endif #ifdef _FLW_CRUMBLING diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/matrices.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/matrices.glsl index efbc80b8d..6c0900072 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/matrices.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/matrices.glsl @@ -3,9 +3,17 @@ struct Matrices { vec4 normalA; vec4 normalB; vec4 normalC; + float skyLightScale; + uint sceneID; + float _padding1; + float _padding2; + mat4 lightingSceneMatrix; }; -void _flw_unpackMatrices(in Matrices mats, out mat4 pose, out mat3 normal) { +void _flw_unpackMatrices(in Matrices mats, out mat4 pose, out mat3 normal, out uint lightingSceneId, out float skyLightScale, out mat4 lightingSceneMatrix) { pose = mats.pose; normal = mat3(mats.normalA.xyz, mats.normalB.xyz, mats.normalC.xyz); + lightingSceneId = mats.sceneID; + skyLightScale = mats.skyLightScale; + lightingSceneMatrix = mats.lightingSceneMatrix; } diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/instancing/main.vert b/common/src/backend/resources/assets/flywheel/flywheel/internal/instancing/main.vert index 5f07651fe..ae6423be6 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/instancing/main.vert +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/instancing/main.vert @@ -8,6 +8,9 @@ uniform int _flw_baseInstance = 0; #ifdef FLW_EMBEDDED uniform mat4 _flw_modelMatrixUniform; uniform mat3 _flw_normalMatrixUniform; +uniform uint _flw_lightingSceneUniform; +uniform float _flw_lightingSkyLightScaleUniform; +uniform mat4 _flw_lightingSceneMatrixUniform; #endif uniform uint _flw_baseVertex; @@ -20,6 +23,9 @@ void main() { #ifdef FLW_EMBEDDED _flw_modelMatrix = _flw_modelMatrixUniform; _flw_normalMatrix = _flw_normalMatrixUniform; + _flw_lightingSceneMatrix = _flw_lightingSceneMatrixUniform; + _flw_lightingSceneId = _flw_lightingSceneUniform; + _flw_skyLightScale = _flw_lightingSkyLightScaleUniform; #endif _flw_main(instance, uint(gl_InstanceID), _flw_baseVertex); diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/light_lut.glsl b/common/src/backend/resources/assets/flywheel/flywheel/internal/light_lut.glsl index 3c2a7e796..5f83289ef 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/light_lut.glsl +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/light_lut.glsl @@ -44,9 +44,14 @@ bool _flw_nextLut(uint base, int coord, out uint next) { return false; } -bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) { +bool _flw_chunkCoordToSectionIndex(uint sceneId, ivec3 sectionPos, out uint index) { + uint scene; + if (_flw_nextLut(0u, int(sceneId), scene) || scene == 0u) { + return true; + } + uint first; - if (_flw_nextLut(0u, sectionPos.y, first) || first == 0u) { + if (_flw_nextLut(scene, sectionPos.y, first) || first == 0u) { return true; } @@ -90,9 +95,9 @@ bool _flw_isSolid(uint sectionOffset, uvec3 blockInSectionPos) { return (word & (1u << bitInWordOffset)) != 0u; } -bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) { +bool flw_lightFetch(uint scene, ivec3 blockPos, out vec2 lightCoord) { uint lightSectionIndex; - if (_flw_chunkCoordToSectionIndex(blockPos >> 4, lightSectionIndex)) { + if (_flw_chunkCoordToSectionIndex(scene, blockPos >> 4, lightSectionIndex)) { return false; } // The offset of the section in the light buffer. @@ -307,14 +312,14 @@ vec3 _flw_lightForDirection(uint[27] lights, vec3 interpolant, uint c00, uint c0 return light; } -bool flw_light(vec3 worldPos, vec3 normal, out FlwLightAo light) { +bool flw_light(uint scene, vec3 worldPos, vec3 normal, ivec3 renderOrigin, out FlwLightAo light) { // Always use the section of the block we are contained in to ensure accuracy. // We don't want to interpolate between sections, but also we might not be able // to rely on the existence neighboring sections, so don't do any extra rounding here. - ivec3 blockPos = ivec3(floor(worldPos)) + flw_renderOrigin; + ivec3 blockPos = ivec3(floor(worldPos)) + renderOrigin; uint lightSectionIndex; - if (_flw_chunkCoordToSectionIndex(blockPos >> 4, lightSectionIndex)) { + if (_flw_chunkCoordToSectionIndex(scene, blockPos >> 4, lightSectionIndex)) { return false; } // The offset of the section in the light buffer. diff --git a/common/src/lib/resources/assets/flywheel/flywheel/light/flat.glsl b/common/src/lib/resources/assets/flywheel/flywheel/light/flat.glsl index 017929019..faa5767e6 100644 --- a/common/src/lib/resources/assets/flywheel/flywheel/light/flat.glsl +++ b/common/src/lib/resources/assets/flywheel/flywheel/light/flat.glsl @@ -1,6 +1,34 @@ +#ifdef FLW_EMBEDDED +flat in float flw_skyLightScale; +flat in uint flw_vertexLightingSceneId; +in vec4 flw_vertexLightingPos; +#endif + void flw_shaderLight() { vec2 embeddedLight; - if (flw_lightFetch(ivec3(floor(flw_vertexPos.xyz)) + flw_renderOrigin, embeddedLight)) { + + uint sceneId = 0; + vec4 vertexLightingPos; + ivec3 renderOrigin; + + #ifdef FLW_EMBEDDED + renderOrigin = flw_renderOrigin; + sceneId = flw_vertexLightingSceneId; + vertexLightingPos = flw_vertexLightingPos; + + if (sceneId != 0) { + renderOrigin = ivec3(0); + } + #else + renderOrigin = flw_renderOrigin; + vertexLightingPos = flw_vertexPos; + #endif + + if (flw_lightFetch(sceneId, ivec3(floor(vertexLightingPos.xyz)) + renderOrigin, embeddedLight)) { flw_fragLight = max(flw_fragLight, embeddedLight); } + + #ifdef FLW_EMBEDDED + flw_fragLight.y *= flw_skyLightScale; + #endif } diff --git a/common/src/lib/resources/assets/flywheel/flywheel/light/smooth.glsl b/common/src/lib/resources/assets/flywheel/flywheel/light/smooth.glsl index c481ae6d7..d0cd80a77 100644 --- a/common/src/lib/resources/assets/flywheel/flywheel/light/smooth.glsl +++ b/common/src/lib/resources/assets/flywheel/flywheel/light/smooth.glsl @@ -1,10 +1,37 @@ +#ifdef FLW_EMBEDDED +flat in float flw_skyLightScale; +flat in uint flw_vertexLightingSceneId; +in vec4 flw_vertexLightingPos; +#endif + void flw_shaderLight() { + uint sceneId = 0; + vec4 vertexLightingPos; + ivec3 renderOrigin; + + #ifdef FLW_EMBEDDED + renderOrigin = flw_renderOrigin; + sceneId = flw_vertexLightingSceneId; + vertexLightingPos = flw_vertexLightingPos; + + if (sceneId != 0) { + renderOrigin = ivec3(0); + } + #else + renderOrigin = flw_renderOrigin; + vertexLightingPos = flw_vertexPos; + #endif + FlwLightAo light; - if (flw_light(flw_vertexPos.xyz, flw_vertexNormal, light)) { + if (flw_light(sceneId, vertexLightingPos.xyz, flw_vertexNormal, renderOrigin, light)) { flw_fragLight = max(flw_fragLight, light.light); if (flw_material.ambientOcclusion) { flw_fragColor.rgb *= light.ao; } } + + #ifdef FLW_EMBEDDED + flw_fragLight.y *= flw_skyLightScale; + #endif } diff --git a/common/src/lib/resources/assets/flywheel/flywheel/light/smooth_when_embedded.glsl b/common/src/lib/resources/assets/flywheel/flywheel/light/smooth_when_embedded.glsl index 439eac214..b3b81e50d 100644 --- a/common/src/lib/resources/assets/flywheel/flywheel/light/smooth_when_embedded.glsl +++ b/common/src/lib/resources/assets/flywheel/flywheel/light/smooth_when_embedded.glsl @@ -1,12 +1,26 @@ +#ifdef FLW_EMBEDDED +flat in float flw_skyLightScale; +flat in uint flw_vertexLightingSceneId; +in vec4 flw_vertexLightingPos; +#endif + void flw_shaderLight() { #ifdef FLW_EMBEDDED + ivec3 renderOrigin = flw_renderOrigin; + + if (flw_vertexLightingSceneId != 0) { + renderOrigin = ivec3(0); + } + FlwLightAo light; - if (flw_light(flw_vertexPos.xyz, flw_vertexNormal, light)) { + if (flw_light(flw_vertexLightingSceneId, flw_vertexLightingPos.xyz, flw_vertexNormal, renderOrigin, light)) { flw_fragLight = max(flw_fragLight, light.light); if (flw_material.ambientOcclusion) { flw_fragColor.rgb *= light.ao; } } + + flw_fragLight.y *= flw_skyLightScale; #endif } diff --git a/docs/flywheel/api/stage/common.glsl b/docs/flywheel/api/stage/common.glsl index 4b073f1db..1672fd23e 100644 --- a/docs/flywheel/api/stage/common.glsl +++ b/docs/flywheel/api/stage/common.glsl @@ -5,8 +5,8 @@ struct FlwLightAo { /// Get the light at the given world position. /// This may be interpolated for smooth lighting. -bool flw_light(vec3 worldPos, vec3 normal, out FlwLightAo light); +bool flw_light(uint scene, vec3 worldPos, vec3 normal, ivec3 renderOrigin, out FlwLightAo light); /// Fetches the light value at the given block position. /// Returns false if the light for the given block is not available. -bool flw_lightFetch(ivec3 blockPos, out vec2 light); +bool flw_lightFetch(uint scene, ivec3 blockPos, out vec2 light); diff --git a/gradle.properties b/gradle.properties index 02295768e..4d930f7e9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ flywheel_semver_version_range=>=1.0.0 <2.0.0 minecraft_semver_version_range = >=1.21.1 <1.21.2 minecraft_maven_version_range = [1.21.1,1.21.2) fabric_api_version_range = >=0.105.0 -neoforge_version_range = [21.1.66,) +neoforge_version_range = [21.1.152,) # General build dependency versions java_version = 21 @@ -34,7 +34,7 @@ parchment_version = 2024.07.07 # Minecraft build dependency versions minecraft_version = 1.21.1 -neoforge_version = 21.1.66 +neoforge_version = 21.1.152 fabric_loader_version = 0.16.5 fabric_api_version = 0.105.0+1.21.1