mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-14 08:16:13 +01:00
Installing a light switch
- Guard 3 different flw_light impls via #define - Guard the inner face correction behind another #define - Add LightSmoothness enum to decide which flw_light impl to use - Make LightSmoothness configurable via a new BackendConfig - Add command to switch LightSmoothness on the fly - Note: currently requires a resource reload so we don't need to compile 4x as many shaders
This commit is contained in:
parent
3dc4cf0841
commit
744c40a56a
16 changed files with 354 additions and 13 deletions
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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();
|
||||||
|
}
|
|
@ -9,4 +9,6 @@ public interface FlwBackendXplat {
|
||||||
FlwBackendXplat INSTANCE = DependencyInjection.load(FlwBackendXplat.class, "dev.engine_room.flywheel.backend.FlwBackendXplatImpl");
|
FlwBackendXplat INSTANCE = DependencyInjection.load(FlwBackendXplat.class, "dev.engine_room.flywheel.backend.FlwBackendXplatImpl");
|
||||||
|
|
||||||
int getLightEmission(BlockState state, BlockGetter level, BlockPos pos);
|
int getLightEmission(BlockState state, BlockGetter level, BlockPos pos);
|
||||||
|
|
||||||
|
BackendConfig getConfig();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<LightSmoothness> {
|
||||||
|
public static final LightSmoothnessArgument INSTANCE = new LightSmoothnessArgument();
|
||||||
|
public static final SingletonArgumentInfo<LightSmoothnessArgument> INFO = SingletonArgumentInfo.contextFree(() -> INSTANCE);
|
||||||
|
|
||||||
|
public LightSmoothnessArgument() {
|
||||||
|
super(LightSmoothness.CODEC, LightSmoothness::values);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<LightSmoothness> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.Flywheel;
|
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.InternalVertex;
|
||||||
import dev.engine_room.flywheel.backend.Samplers;
|
import dev.engine_room.flywheel.backend.Samplers;
|
||||||
import dev.engine_room.flywheel.backend.compile.component.InstanceStructComponent;
|
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");
|
private static final ResourceLocation API_IMPL_FRAG = Flywheel.rl("internal/api_impl.frag");
|
||||||
|
|
||||||
static CompilationHarness<PipelineProgramKey> create(ShaderSources sources, Pipeline pipeline, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents, Collection<String> extensions) {
|
static CompilationHarness<PipelineProgramKey> create(ShaderSources sources, Pipeline pipeline, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents, Collection<String> 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()
|
return PIPELINE.program()
|
||||||
.link(PIPELINE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX)
|
.link(PIPELINE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX)
|
||||||
.nameMapper(key -> {
|
.nameMapper(key -> {
|
||||||
|
@ -38,6 +42,7 @@ public final class PipelineCompiler {
|
||||||
.requireExtensions(extensions)
|
.requireExtensions(extensions)
|
||||||
.onCompile((key, comp) -> key.contextShader()
|
.onCompile((key, comp) -> key.contextShader()
|
||||||
.onCompile(comp))
|
.onCompile(comp))
|
||||||
|
.onCompile((key, comp) -> lightSmoothness.onCompile(comp))
|
||||||
.withResource(API_IMPL_VERT)
|
.withResource(API_IMPL_VERT)
|
||||||
.withComponent(key -> new InstanceStructComponent(key.instanceType()))
|
.withComponent(key -> new InstanceStructComponent(key.instanceType()))
|
||||||
.withResource(key -> key.instanceType()
|
.withResource(key -> key.instanceType()
|
||||||
|
@ -57,6 +62,7 @@ public final class PipelineCompiler {
|
||||||
.enableExtension("GL_ARB_conservative_depth")
|
.enableExtension("GL_ARB_conservative_depth")
|
||||||
.onCompile((key, comp) -> key.contextShader()
|
.onCompile((key, comp) -> key.contextShader()
|
||||||
.onCompile(comp))
|
.onCompile(comp))
|
||||||
|
.onCompile((key, comp) -> lightSmoothness.onCompile(comp))
|
||||||
.withResource(API_IMPL_FRAG)
|
.withResource(API_IMPL_FRAG)
|
||||||
.withComponents(fragmentComponents)
|
.withComponents(fragmentComponents)
|
||||||
.withResource(pipeline.fragmentMain()))
|
.withResource(pipeline.fragmentMain()))
|
||||||
|
|
|
@ -153,7 +153,7 @@ uint _flw_fetchSolid3x3x3(uint sectionOffset, ivec3 blockInSectionPos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#define _flw_index3x3x3(x, y, z) ((x) + (z) * 3u + (y) * 9u)
|
#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)
|
#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.
|
/// 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];
|
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;
|
vec3[8] adjusted;
|
||||||
for (uint i = 0; i < 8; i++) {
|
for (uint i = 0; i < 8; i++) {
|
||||||
uint validCount = (summed[i] >> 20u) & 0x3FFu;
|
#ifdef _FLW_INNER_FACE_CORRECTION
|
||||||
// Always use the AO from the actual corner
|
|
||||||
adjusted[i].z = float(validCount);
|
|
||||||
|
|
||||||
// If the current corner has no valid blocks, use the opposite
|
// If the current corner has no valid blocks, use the opposite
|
||||||
// corner's light based on which direction we're evaluating.
|
// 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.
|
// 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 & 0x3FFu, (corner >> 10u) & 0x3FFu, corner >> 20u);
|
||||||
uvec3 unpacked = uvec3(corner, corner >> 10u, corner >> 20u) & 0x3FFu;
|
|
||||||
|
|
||||||
// ...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].xy = vec2(unpacked.xy) * normalizers[unpacked.z];
|
||||||
|
adjusted[i].z = float(unpacked.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trilinear interpolation, including valid count
|
// Trilinear interpolation, including valid count
|
||||||
|
@ -234,7 +235,6 @@ vec3 _flw_lightForDirection(uint[27] lights, vec3 interpolant, uvec3 c00, uvec3
|
||||||
return light;
|
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) {
|
bool flw_light(vec3 worldPos, vec3 normal, out FlwLightAo light) {
|
||||||
// Always use the section of the block we are contained in to ensure accuracy.
|
// 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
|
// 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
|
// The block's position in the section adjusted into 18x18x18 space
|
||||||
ivec3 blockInSectionPos = (blockPos & 0xF) + 1;
|
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);
|
uint solid = _flw_fetchSolid3x3x3(sectionOffset, blockInSectionPos);
|
||||||
|
|
||||||
if (solid == _FLW_COMPLETELY_SOLID) {
|
if (solid == _FLW_COMPLETELY_SOLID) {
|
||||||
|
@ -301,6 +335,13 @@ bool flw_light(vec3 worldPos, vec3 normal, out FlwLightAo light) {
|
||||||
light.light = lightAo.xy;
|
light.light = lightAo.xy;
|
||||||
light.ao = lightAo.z;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,12 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.math.Axis;
|
import com.mojang.math.Axis;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.instance.Instance;
|
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.api.visualization.VisualizationContext;
|
||||||
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
|
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
|
||||||
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
||||||
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
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.material.SimpleMaterial;
|
||||||
import dev.engine_room.flywheel.lib.model.ModelCache;
|
import dev.engine_room.flywheel.lib.model.ModelCache;
|
||||||
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
|
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
|
||||||
|
@ -19,17 +21,20 @@ import dev.engine_room.flywheel.lib.model.part.ModelPartConverter;
|
||||||
import dev.engine_room.flywheel.lib.transform.TransformStack;
|
import dev.engine_room.flywheel.lib.transform.TransformStack;
|
||||||
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
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.model.geom.ModelLayers;
|
||||||
import net.minecraft.client.renderer.Sheets;
|
import net.minecraft.client.renderer.Sheets;
|
||||||
import net.minecraft.client.resources.model.Material;
|
import net.minecraft.client.resources.model.Material;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.world.item.DyeColor;
|
import net.minecraft.world.item.DyeColor;
|
||||||
import net.minecraft.world.level.block.ShulkerBoxBlock;
|
import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||||
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||||
|
|
||||||
public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockEntity> implements SimpleDynamicVisual {
|
public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockEntity> implements SimpleDynamicVisual, ShaderLightVisual {
|
||||||
private static final dev.engine_room.flywheel.api.material.Material MATERIAL = SimpleMaterial.builder()
|
private static final dev.engine_room.flywheel.api.material.Material MATERIAL = SimpleMaterial.builder()
|
||||||
.cutout(CutoutShaders.ONE_TENTH)
|
.cutout(CutoutShaders.ONE_TENTH)
|
||||||
|
.light(LightShaders.SMOOTH)
|
||||||
.texture(Sheets.SHULKER_SHEET)
|
.texture(Sheets.SHULKER_SHEET)
|
||||||
.mipmap(false)
|
.mipmap(false)
|
||||||
.backfaceCulling(false)
|
.backfaceCulling(false)
|
||||||
|
@ -67,6 +72,7 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
|
||||||
.translate(getVisualPosition())
|
.translate(getVisualPosition())
|
||||||
.translate(0.5f)
|
.translate(0.5f)
|
||||||
.scale(0.9995f)
|
.scale(0.9995f)
|
||||||
|
.scale(11f)
|
||||||
.rotate(rotation)
|
.rotate(rotation)
|
||||||
.scale(1, -1, -1)
|
.scale(1, -1, -1)
|
||||||
.translateY(-1);
|
.translateY(-1);
|
||||||
|
@ -120,9 +126,26 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
|
||||||
stack.popPose();
|
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
|
@Override
|
||||||
public void updateLight(float partialTick) {
|
public void updateLight(float partialTick) {
|
||||||
relight(base, lid);
|
// relight(base, lid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,4 +9,9 @@ public class FlwBackendXplatImpl implements FlwBackendXplat {
|
||||||
public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) {
|
public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) {
|
||||||
return state.getLightEmission();
|
return state.getLightEmission();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BackendConfig getConfig() {
|
||||||
|
return FabricBackendConfig.INSTANCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ import com.mojang.brigadier.context.CommandContext;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.backend.Backend;
|
import dev.engine_room.flywheel.api.backend.Backend;
|
||||||
import dev.engine_room.flywheel.api.backend.BackendManager;
|
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.DebugMode;
|
||||||
import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms;
|
import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms;
|
||||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
|
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
|
||||||
|
@ -123,6 +126,21 @@ public final class FlwCommands {
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
command.then(ClientCommandManager.literal("lightSmoothness")
|
||||||
|
.then(ClientCommandManager.argument("mode", LightSmoothnessArgument.INSTANCE)
|
||||||
|
.executes(context -> {
|
||||||
|
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);
|
dispatcher.register(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.jetbrains.annotations.UnknownNullability;
|
||||||
import dev.engine_room.flywheel.api.Flywheel;
|
import dev.engine_room.flywheel.api.Flywheel;
|
||||||
import dev.engine_room.flywheel.api.event.EndClientResourceReloadCallback;
|
import dev.engine_room.flywheel.api.event.EndClientResourceReloadCallback;
|
||||||
import dev.engine_room.flywheel.api.event.ReloadLevelRendererCallback;
|
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.compile.FlwProgramsReloader;
|
||||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||||
import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
||||||
|
@ -62,6 +63,7 @@ public final class FlywheelFabric implements ClientModInitializer {
|
||||||
|
|
||||||
ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("backend"), BackendArgument.class, BackendArgument.INFO);
|
ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("backend"), BackendArgument.class, BackendArgument.INFO);
|
||||||
ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("debug_mode"), DebugModeArgument.class, DebugModeArgument.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() {
|
private static void setupLib() {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.event.EndClientResourceReloadCallback;
|
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.FabricFlwConfig;
|
||||||
import dev.engine_room.flywheel.impl.FlwImpl;
|
import dev.engine_room.flywheel.impl.FlwImpl;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
@ -28,6 +29,7 @@ abstract class MinecraftMixin {
|
||||||
// Load the config after we freeze registries,
|
// Load the config after we freeze registries,
|
||||||
// so we can find third party backends.
|
// so we can find third party backends.
|
||||||
FabricFlwConfig.INSTANCE.load();
|
FabricFlwConfig.INSTANCE.load();
|
||||||
|
FabricBackendConfig.INSTANCE.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "method_24040", at = @At("HEAD"))
|
@Inject(method = "method_24040", at = @At("HEAD"))
|
||||||
|
|
|
@ -9,4 +9,9 @@ public class FlwBackendXplatImpl implements FlwBackendXplat {
|
||||||
public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) {
|
public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) {
|
||||||
return state.getLightEmission(level, pos);
|
return state.getLightEmission(level, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BackendConfig getConfig() {
|
||||||
|
return ForgeBackendConfig.INSTANCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<ClientConfig, ForgeConfigSpec> 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> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,9 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.backend.Backend;
|
import dev.engine_room.flywheel.api.backend.Backend;
|
||||||
import dev.engine_room.flywheel.api.backend.BackendManager;
|
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.DebugMode;
|
||||||
import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms;
|
import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
@ -121,6 +124,21 @@ public final class FlwCommands {
|
||||||
return Command.SINGLE_SUCCESS;
|
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);
|
event.getDispatcher().register(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import org.jetbrains.annotations.UnknownNullability;
|
||||||
import dev.engine_room.flywheel.api.Flywheel;
|
import dev.engine_room.flywheel.api.Flywheel;
|
||||||
import dev.engine_room.flywheel.api.event.EndClientResourceReloadEvent;
|
import dev.engine_room.flywheel.api.event.EndClientResourceReloadEvent;
|
||||||
import dev.engine_room.flywheel.api.event.ReloadLevelRendererEvent;
|
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.compile.FlwProgramsReloader;
|
||||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||||
import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
||||||
|
@ -52,6 +54,7 @@ public final class FlywheelForge {
|
||||||
.getModEventBus();
|
.getModEventBus();
|
||||||
|
|
||||||
ForgeFlwConfig.INSTANCE.registerSpecs(modLoadingContext);
|
ForgeFlwConfig.INSTANCE.registerSpecs(modLoadingContext);
|
||||||
|
ForgeBackendConfig.INSTANCE.registerSpecs(modLoadingContext);
|
||||||
|
|
||||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> FlywheelForge.clientInit(forgeEventBus, modEventBus));
|
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> FlywheelForge.clientInit(forgeEventBus, modEventBus));
|
||||||
}
|
}
|
||||||
|
@ -94,11 +97,13 @@ public final class FlywheelForge {
|
||||||
modEventBus.addListener((FMLCommonSetupEvent e) -> {
|
modEventBus.addListener((FMLCommonSetupEvent e) -> {
|
||||||
ArgumentTypeInfos.registerByClass(BackendArgument.class, BackendArgument.INFO);
|
ArgumentTypeInfos.registerByClass(BackendArgument.class, BackendArgument.INFO);
|
||||||
ArgumentTypeInfos.registerByClass(DebugModeArgument.class, DebugModeArgument.INFO);
|
ArgumentTypeInfos.registerByClass(DebugModeArgument.class, DebugModeArgument.INFO);
|
||||||
|
ArgumentTypeInfos.registerByClass(LightSmoothnessArgument.class, LightSmoothnessArgument.INFO);
|
||||||
});
|
});
|
||||||
modEventBus.addListener((RegisterEvent e) -> {
|
modEventBus.addListener((RegisterEvent e) -> {
|
||||||
if (e.getRegistryKey().equals(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES)) {
|
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("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("debug_mode"), () -> DebugModeArgument.INFO);
|
||||||
|
e.register(ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, Flywheel.rl("light_smoothness"), () -> LightSmoothnessArgument.INFO);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue