diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/BackendConfig.java b/common/src/backend/java/dev/engine_room/flywheel/backend/BackendConfig.java new file mode 100644 index 000000000..1bc137773 --- /dev/null +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/BackendConfig.java @@ -0,0 +1,17 @@ +package dev.engine_room.flywheel.backend; + +import dev.engine_room.flywheel.backend.compile.LightSmoothness; + +public interface BackendConfig { + BackendConfig INSTANCE = FlwBackendXplat.INSTANCE.getConfig(); + + /** + * How smooth/accurate our flw_light impl is. + * + *

This makes more sense here as a backend-specific config because it's tightly coupled to + * our backend's implementation. 3rd party backend may have different approaches and configurations. + * + * @return The current light smoothness setting. + */ + LightSmoothness lightSmoothness(); +} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplat.java b/common/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplat.java index 6acb2832a..8cd216c59 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplat.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplat.java @@ -9,4 +9,6 @@ public interface FlwBackendXplat { FlwBackendXplat INSTANCE = DependencyInjection.load(FlwBackendXplat.class, "dev.engine_room.flywheel.backend.FlwBackendXplatImpl"); int getLightEmission(BlockState state, BlockGetter level, BlockPos pos); + + BackendConfig getConfig(); } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/LightSmoothnessArgument.java b/common/src/backend/java/dev/engine_room/flywheel/backend/LightSmoothnessArgument.java new file mode 100644 index 000000000..871184d5d --- /dev/null +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/LightSmoothnessArgument.java @@ -0,0 +1,14 @@ +package dev.engine_room.flywheel.backend; + +import dev.engine_room.flywheel.backend.compile.LightSmoothness; +import net.minecraft.commands.arguments.StringRepresentableArgument; +import net.minecraft.commands.synchronization.SingletonArgumentInfo; + +public class LightSmoothnessArgument extends StringRepresentableArgument { + public static final LightSmoothnessArgument INSTANCE = new LightSmoothnessArgument(); + public static final SingletonArgumentInfo INFO = SingletonArgumentInfo.contextFree(() -> INSTANCE); + + public LightSmoothnessArgument() { + super(LightSmoothness.CODEC, LightSmoothness::values); + } +} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/LightSmoothness.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/LightSmoothness.java new file mode 100644 index 000000000..4ff0b8459 --- /dev/null +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/LightSmoothness.java @@ -0,0 +1,38 @@ +package dev.engine_room.flywheel.backend.compile; + +import java.util.Locale; + +import com.mojang.serialization.Codec; + +import dev.engine_room.flywheel.backend.compile.core.Compilation; +import net.minecraft.util.StringRepresentable; + +public enum LightSmoothness implements StringRepresentable { + FLAT(0, false), + TRI_LINEAR(1, false), + SMOOTH(2, false), + SMOOTH_INNER_FACE_CORRECTED(2, true), + ; + + public static final Codec CODEC = StringRepresentable.fromEnum(LightSmoothness::values); + + private final int smoothnessDefine; + private final boolean innerFaceCorrection; + + LightSmoothness(int smoothnessDefine, boolean innerFaceCorrection) { + this.smoothnessDefine = smoothnessDefine; + this.innerFaceCorrection = innerFaceCorrection; + } + + public void onCompile(Compilation comp) { + comp.define("_FLW_LIGHT_SMOOTHNESS", Integer.toString(smoothnessDefine)); + if (innerFaceCorrection) { + comp.define("_FLW_INNER_FACE_CORRECTION"); + } + } + + @Override + public String getSerializedName() { + return name().toLowerCase(Locale.ROOT); + } +} diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java index a8d1652cf..bc303f8f9 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/compile/PipelineCompiler.java @@ -4,6 +4,7 @@ import java.util.List; import dev.engine_room.flywheel.api.Flywheel; +import dev.engine_room.flywheel.backend.BackendConfig; import dev.engine_room.flywheel.backend.InternalVertex; import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.compile.component.InstanceStructComponent; @@ -25,6 +26,9 @@ public final class PipelineCompiler { private static final ResourceLocation API_IMPL_FRAG = Flywheel.rl("internal/api_impl.frag"); static CompilationHarness create(ShaderSources sources, Pipeline pipeline, List vertexComponents, List fragmentComponents, Collection extensions) { + // We could technically compile every version of light smoothness ahead of time, + // but that seems unnecessary as I doubt most folks will be changing this option often. + var lightSmoothness = BackendConfig.INSTANCE.lightSmoothness(); return PIPELINE.program() .link(PIPELINE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX) .nameMapper(key -> { @@ -38,6 +42,7 @@ static CompilationHarness create(ShaderSources sources, Pipe .requireExtensions(extensions) .onCompile((key, comp) -> key.contextShader() .onCompile(comp)) + .onCompile((key, comp) -> lightSmoothness.onCompile(comp)) .withResource(API_IMPL_VERT) .withComponent(key -> new InstanceStructComponent(key.instanceType())) .withResource(key -> key.instanceType() @@ -57,6 +62,7 @@ static CompilationHarness create(ShaderSources sources, Pipe .enableExtension("GL_ARB_conservative_depth") .onCompile((key, comp) -> key.contextShader() .onCompile(comp)) + .onCompile((key, comp) -> lightSmoothness.onCompile(comp)) .withResource(API_IMPL_FRAG) .withComponents(fragmentComponents) .withResource(pipeline.fragmentMain())) 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 63d555708..d537f4515 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 @@ -153,7 +153,7 @@ uint _flw_fetchSolid3x3x3(uint sectionOffset, ivec3 blockInSectionPos) { } #define _flw_index3x3x3(x, y, z) ((x) + (z) * 3u + (y) * 9u) -#define _flw_index3x3x3v(p) _flw_index3x3x3((p.x), (p.y), (p.z)) +#define _flw_index3x3x3v(p) _flw_index3x3x3((p).x, (p).y, (p).z) #define _flw_validCountToAo(validCount) (1. - (4. - (validCount)) * 0.2) /// Calculate the light for a direction by averaging the light at the corners of the block. @@ -196,23 +196,24 @@ vec3 _flw_lightForDirection(uint[27] lights, vec3 interpolant, uvec3 c00, uvec3 summed[i] = lights[ic00 + corner] + lights[ic01 + corner] + lights[ic10 + corner] + lights[ic11 + corner]; } - // The final light and AO value for each corner. + // The final light and number of valid blocks for each corner. vec3[8] adjusted; for (uint i = 0; i < 8; i++) { - uint validCount = (summed[i] >> 20u) & 0x3FFu; - // Always use the AO from the actual corner - adjusted[i].z = float(validCount); - + #ifdef _FLW_INNER_FACE_CORRECTION // If the current corner has no valid blocks, use the opposite // corner's light based on which direction we're evaluating. // Because of how our corners are indexed, moving along one axis is the same as flipping a bit. - uint corner = summed[(validCount == 0 ? i ^ oppositeMask : i)]; + uint cornerIndex = (summed[i] & 0xFFF00000u) == 0u ? i ^ oppositeMask : i; + #else + uint cornerIndex = i; + #endif + uint corner = summed[cornerIndex]; - // Still need to unpack all 3 fields of the maybe opposite corner so we can... - uvec3 unpacked = uvec3(corner, corner >> 10u, corner >> 20u) & 0x3FFu; + uvec3 unpacked = uvec3(corner & 0x3FFu, (corner >> 10u) & 0x3FFu, corner >> 20u); - // ...normalize by the number of valid blocks. + // Normalize by the number of valid blocks. adjusted[i].xy = vec2(unpacked.xy) * normalizers[unpacked.z]; + adjusted[i].z = float(unpacked.z); } // Trilinear interpolation, including valid count @@ -234,7 +235,6 @@ vec3 _flw_lightForDirection(uint[27] lights, vec3 interpolant, uvec3 c00, uvec3 return light; } -// TODO: Add config for light smoothness. Should work at a compile flag level bool flw_light(vec3 worldPos, vec3 normal, 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 @@ -251,6 +251,40 @@ bool flw_light(vec3 worldPos, vec3 normal, out FlwLightAo light) { // The block's position in the section adjusted into 18x18x18 space ivec3 blockInSectionPos = (blockPos & 0xF) + 1; + #if _FLW_LIGHT_SMOOTHNESS == 1// Directly trilerp as if sampling a texture + + // The lowest corner of the 2x2x2 area we'll be trilinear interpolating. + // The ugly bit on the end evaluates to -1 or 0 depending on which side of 0.5 we are. + uvec3 lowestCorner = blockInSectionPos + ivec3(floor(fract(worldPos) - 0.5)); + + // The distance our fragment is from the center of the lowest corner. + vec3 interpolant = fract(worldPos - 0.5); + + // Fetch everything for trilinear interpolation + // Hypothetically we could re-order these and do some calculations in-between fetches + // to help with latency hiding, but the compiler should be able to do that for us. + vec2 light000 = vec2(_flw_lightAt(sectionOffset, lowestCorner)); + vec2 light100 = vec2(_flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 0, 0))); + vec2 light001 = vec2(_flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 0, 1))); + vec2 light101 = vec2(_flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 0, 1))); + vec2 light010 = vec2(_flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 1, 0))); + vec2 light110 = vec2(_flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 1, 0))); + vec2 light011 = vec2(_flw_lightAt(sectionOffset, lowestCorner + uvec3(0, 1, 1))); + vec2 light111 = vec2(_flw_lightAt(sectionOffset, lowestCorner + uvec3(1, 1, 1))); + + vec2 light00 = mix(light000, light001, interpolant.z); + vec2 light01 = mix(light010, light011, interpolant.z); + vec2 light10 = mix(light100, light101, interpolant.z); + vec2 light11 = mix(light110, light111, interpolant.z); + + vec2 light0 = mix(light00, light01, interpolant.y); + vec2 light1 = mix(light10, light11, interpolant.y); + + light.light = mix(light0, light1, interpolant.x) / 15.; + light.ao = 1.; + + #elif _FLW_LIGHT_SMOOTHNESS == 2// Lighting and AO accurate to chunk baking + uint solid = _flw_fetchSolid3x3x3(sectionOffset, blockInSectionPos); if (solid == _FLW_COMPLETELY_SOLID) { @@ -301,6 +335,13 @@ bool flw_light(vec3 worldPos, vec3 normal, out FlwLightAo light) { light.light = lightAo.xy; light.ao = lightAo.z; + #else// Entirely flat lighting, the lowest setting and a fallback in case an invalid option is set + + light.light = vec2(_flw_lightAt(sectionOffset, blockInSectionPos)) / 15.; + light.ao = 1.; + + #endif + return true; } diff --git a/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java b/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java index 9e9e17925..716b8a317 100644 --- a/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java +++ b/common/src/main/java/dev/engine_room/flywheel/vanilla/ShulkerBoxVisual.java @@ -8,10 +8,12 @@ import com.mojang.math.Axis; import dev.engine_room.flywheel.api.instance.Instance; +import dev.engine_room.flywheel.api.visual.ShaderLightVisual; import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.lib.instance.InstanceTypes; import dev.engine_room.flywheel.lib.instance.TransformedInstance; import dev.engine_room.flywheel.lib.material.CutoutShaders; +import dev.engine_room.flywheel.lib.material.LightShaders; import dev.engine_room.flywheel.lib.material.SimpleMaterial; import dev.engine_room.flywheel.lib.model.ModelCache; import dev.engine_room.flywheel.lib.model.SingleMeshModel; @@ -19,17 +21,20 @@ import dev.engine_room.flywheel.lib.transform.TransformStack; import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual; import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual; +import it.unimi.dsi.fastutil.longs.LongArraySet; import net.minecraft.client.model.geom.ModelLayers; import net.minecraft.client.renderer.Sheets; import net.minecraft.client.resources.model.Material; import net.minecraft.core.Direction; +import net.minecraft.core.SectionPos; import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; -public class ShulkerBoxVisual extends AbstractBlockEntityVisual implements SimpleDynamicVisual { +public class ShulkerBoxVisual extends AbstractBlockEntityVisual implements SimpleDynamicVisual, ShaderLightVisual { private static final dev.engine_room.flywheel.api.material.Material MATERIAL = SimpleMaterial.builder() .cutout(CutoutShaders.ONE_TENTH) + .light(LightShaders.SMOOTH) .texture(Sheets.SHULKER_SHEET) .mipmap(false) .backfaceCulling(false) @@ -67,6 +72,7 @@ public ShulkerBoxVisual(VisualizationContext ctx, ShulkerBoxBlockEntity blockEnt .translate(getVisualPosition()) .translate(0.5f) .scale(0.9995f) + .scale(11f) .rotate(rotation) .scale(1, -1, -1) .translateY(-1); @@ -120,9 +126,26 @@ public void beginFrame(Context context) { stack.popPose(); } + @Override + public void setSectionCollector(SectionCollector sectionCollector) { + + var center = SectionPos.asLong(pos); + var out = new LongArraySet(); + + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + out.add(SectionPos.offset(center, x, y, z)); + } + } + } + + sectionCollector.sections(out); + } + @Override public void updateLight(float partialTick) { - relight(base, lid); + // relight(base, lid); } @Override diff --git a/fabric/src/backend/java/dev/engine_room/flywheel/backend/FabricBackendConfig.java b/fabric/src/backend/java/dev/engine_room/flywheel/backend/FabricBackendConfig.java new file mode 100644 index 000000000..68777db07 --- /dev/null +++ b/fabric/src/backend/java/dev/engine_room/flywheel/backend/FabricBackendConfig.java @@ -0,0 +1,106 @@ +package dev.engine_room.flywheel.backend; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.nio.file.Path; +import java.util.Locale; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; + +import dev.engine_room.flywheel.backend.compile.LightSmoothness; +import net.fabricmc.loader.api.FabricLoader; + +public class FabricBackendConfig implements BackendConfig { + + public static final Path PATH = FabricLoader.getInstance() + .getConfigDir() + .resolve("flywheel-backend.json"); + + public static final FabricBackendConfig INSTANCE = new FabricBackendConfig(PATH.toFile()); + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting() + .create(); + + private final File file; + + public LightSmoothness lightSmoothness = LightSmoothness.SMOOTH; + + public FabricBackendConfig(File file) { + this.file = file; + } + + @Override + public LightSmoothness lightSmoothness() { + return lightSmoothness; + } + + public void load() { + if (file.exists()) { + try (FileReader reader = new FileReader(file)) { + fromJson(JsonParser.parseReader(reader)); + } catch (Exception e) { + FlwBackend.LOGGER.warn("Could not load config from file '{}'", file.getAbsolutePath(), e); + } + } + // In case we found an error in the config file, immediately save to fix it. + save(); + } + + public void save() { + try (FileWriter writer = new FileWriter(file)) { + GSON.toJson(toJson(), writer); + } catch (Exception e) { + FlwBackend.LOGGER.warn("Could not save config to file '{}'", file.getAbsolutePath(), e); + } + } + + public void fromJson(JsonElement json) { + if (!(json instanceof JsonObject object)) { + FlwBackend.LOGGER.warn("Config JSON must be an object"); + lightSmoothness = LightSmoothness.SMOOTH; + return; + } + + readLightSmoothness(object); + } + + private void readLightSmoothness(JsonObject object) { + var backendJson = object.get("lightSmoothness"); + String msg = null; + + if (backendJson instanceof JsonPrimitive primitive && primitive.isString()) { + var value = primitive.getAsString(); + + for (var item : LightSmoothness.values()) { + if (item.name() + .equalsIgnoreCase(value)) { + lightSmoothness = item; + return; + } + } + + msg = "Unknown 'lightSmoothness' value: " + value; + } else if (backendJson != null) { + msg = "'lightSmoothness' value must be a string"; + } + + // Don't log an error if the field is missing. + if (msg != null) { + FlwBackend.LOGGER.warn(msg); + } + lightSmoothness = LightSmoothness.SMOOTH; + } + + public JsonObject toJson() { + JsonObject object = new JsonObject(); + object.addProperty("lightSmoothness", lightSmoothness.toString() + .toLowerCase(Locale.ROOT)); + return object; + } +} diff --git a/fabric/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplatImpl.java b/fabric/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplatImpl.java index 661f58bc7..3094b8888 100644 --- a/fabric/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplatImpl.java +++ b/fabric/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplatImpl.java @@ -9,4 +9,9 @@ public class FlwBackendXplatImpl implements FlwBackendXplat { public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) { return state.getLightEmission(); } + + @Override + public BackendConfig getConfig() { + return FabricBackendConfig.INSTANCE; + } } diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java index 04bc2af12..129e0eb3e 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java @@ -8,6 +8,9 @@ import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.api.backend.BackendManager; +import dev.engine_room.flywheel.backend.FabricBackendConfig; +import dev.engine_room.flywheel.backend.LightSmoothnessArgument; +import dev.engine_room.flywheel.backend.compile.LightSmoothness; import dev.engine_room.flywheel.backend.engine.uniform.DebugMode; import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; @@ -123,6 +126,21 @@ public static void registerClientCommands(CommandDispatcher { + var oldValue = FabricBackendConfig.INSTANCE.lightSmoothness; + var newValue = context.getArgument("mode", LightSmoothness.class); + + if (oldValue != newValue) { + FabricBackendConfig.INSTANCE.lightSmoothness = newValue; + FabricBackendConfig.INSTANCE.save(); + Minecraft.getInstance() + .reloadResourcePacks(); + } + return Command.SINGLE_SUCCESS; + }))); + dispatcher.register(command); } diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java index 26db5c4f7..5e8ab76ac 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlywheelFabric.java @@ -5,6 +5,7 @@ import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.event.EndClientResourceReloadCallback; import dev.engine_room.flywheel.api.event.ReloadLevelRendererCallback; +import dev.engine_room.flywheel.backend.LightSmoothnessArgument; import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader; import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler; @@ -62,6 +63,7 @@ private static void setupImpl() { ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("backend"), BackendArgument.class, BackendArgument.INFO); ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("debug_mode"), DebugModeArgument.class, DebugModeArgument.INFO); + ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("light_smoothness"), LightSmoothnessArgument.class, LightSmoothnessArgument.INFO); } private static void setupLib() { diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/MinecraftMixin.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/MinecraftMixin.java index f96a68595..79ec1986b 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/MinecraftMixin.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/MinecraftMixin.java @@ -11,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import dev.engine_room.flywheel.api.event.EndClientResourceReloadCallback; +import dev.engine_room.flywheel.backend.FabricBackendConfig; import dev.engine_room.flywheel.impl.FabricFlwConfig; import dev.engine_room.flywheel.impl.FlwImpl; import net.minecraft.client.Minecraft; @@ -28,6 +29,7 @@ abstract class MinecraftMixin { // Load the config after we freeze registries, // so we can find third party backends. FabricFlwConfig.INSTANCE.load(); + FabricBackendConfig.INSTANCE.load(); } @Inject(method = "method_24040", at = @At("HEAD")) diff --git a/forge/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplatImpl.java b/forge/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplatImpl.java index f49549f3e..6c6bd2c68 100644 --- a/forge/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplatImpl.java +++ b/forge/src/backend/java/dev/engine_room/flywheel/backend/FlwBackendXplatImpl.java @@ -9,4 +9,9 @@ public class FlwBackendXplatImpl implements FlwBackendXplat { public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) { return state.getLightEmission(level, pos); } + + @Override + public BackendConfig getConfig() { + return ForgeBackendConfig.INSTANCE; + } } diff --git a/forge/src/backend/java/dev/engine_room/flywheel/backend/ForgeBackendConfig.java b/forge/src/backend/java/dev/engine_room/flywheel/backend/ForgeBackendConfig.java new file mode 100644 index 000000000..3d3e64963 --- /dev/null +++ b/forge/src/backend/java/dev/engine_room/flywheel/backend/ForgeBackendConfig.java @@ -0,0 +1,39 @@ +package dev.engine_room.flywheel.backend; + +import org.apache.commons.lang3.tuple.Pair; + +import dev.engine_room.flywheel.backend.compile.LightSmoothness; +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.config.ModConfig; + +public class ForgeBackendConfig implements BackendConfig { + public static final ForgeBackendConfig INSTANCE = new ForgeBackendConfig(); + + public final ClientConfig client; + private final ForgeConfigSpec clientSpec; + + private ForgeBackendConfig() { + Pair clientPair = new ForgeConfigSpec.Builder().configure(ClientConfig::new); + this.client = clientPair.getLeft(); + clientSpec = clientPair.getRight(); + } + + @Override + public LightSmoothness lightSmoothness() { + return client.lightSmoothness.get(); + } + + public void registerSpecs(ModLoadingContext context) { + context.registerConfig(ModConfig.Type.CLIENT, clientSpec, "flywheel-backend.toml"); + } + + public static class ClientConfig { + public final ForgeConfigSpec.EnumValue lightSmoothness; + + private ClientConfig(ForgeConfigSpec.Builder builder) { + lightSmoothness = builder.comment("How smooth flywheel's shader-based lighting should be. May have a large performance impact.") + .defineEnum("lightSmoothness", LightSmoothness.SMOOTH); + } + } +} diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java index 8d7ea09b1..640225eca 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java @@ -6,6 +6,9 @@ import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.api.backend.BackendManager; +import dev.engine_room.flywheel.backend.ForgeBackendConfig; +import dev.engine_room.flywheel.backend.LightSmoothnessArgument; +import dev.engine_room.flywheel.backend.compile.LightSmoothness; import dev.engine_room.flywheel.backend.engine.uniform.DebugMode; import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms; import net.minecraft.client.Minecraft; @@ -121,6 +124,21 @@ public static void registerClientCommands(RegisterClientCommandsEvent event) { return Command.SINGLE_SUCCESS; }))); + var lightSmoothnessValue = ForgeBackendConfig.INSTANCE.client.lightSmoothness; + command.then(Commands.literal("lightSmoothness") + .then(Commands.argument("mode", LightSmoothnessArgument.INSTANCE) + .executes(context -> { + var oldValue = lightSmoothnessValue.get(); + var newValue = context.getArgument("mode", LightSmoothness.class); + + if (oldValue != newValue) { + lightSmoothnessValue.set(newValue); + Minecraft.getInstance() + .reloadResourcePacks(); + } + return Command.SINGLE_SUCCESS; + }))); + event.getDispatcher().register(command); } diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java index e9271d865..57756aa47 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java @@ -6,6 +6,8 @@ import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.event.EndClientResourceReloadEvent; import dev.engine_room.flywheel.api.event.ReloadLevelRendererEvent; +import dev.engine_room.flywheel.backend.ForgeBackendConfig; +import dev.engine_room.flywheel.backend.LightSmoothnessArgument; import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader; import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler; @@ -52,6 +54,7 @@ public FlywheelForge() { .getModEventBus(); ForgeFlwConfig.INSTANCE.registerSpecs(modLoadingContext); + ForgeBackendConfig.INSTANCE.registerSpecs(modLoadingContext); DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> FlywheelForge.clientInit(forgeEventBus, modEventBus)); } @@ -94,11 +97,13 @@ private static void registerImplEventListeners(IEventBus forgeEventBus, IEventBu modEventBus.addListener((FMLCommonSetupEvent e) -> { ArgumentTypeInfos.registerByClass(BackendArgument.class, BackendArgument.INFO); ArgumentTypeInfos.registerByClass(DebugModeArgument.class, DebugModeArgument.INFO); + ArgumentTypeInfos.registerByClass(LightSmoothnessArgument.class, LightSmoothnessArgument.INFO); }); modEventBus.addListener((RegisterEvent e) -> { if (e.getRegistryKey().equals(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES)) { e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, Flywheel.rl("backend"), () -> BackendArgument.INFO); e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, Flywheel.rl("debug_mode"), () -> DebugModeArgument.INFO); + e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, Flywheel.rl("light_smoothness"), () -> LightSmoothnessArgument.INFO); } }); }