Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Bedrock Sun Glare and Sky Color #224

Merged
merged 6 commits into from
Feb 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'fabric-loom' version '0.12.47'
id 'fabric-loom' version '1.1-SNAPSHOT'
id 'maven-publish'
}

Expand All @@ -24,6 +24,8 @@ loom {
runDir "build/datagen"
}
}

createRemapConfigurations(sourceSets.test)
}

sourceSets {
Expand All @@ -43,6 +45,7 @@ repositories {
}
maven { url "https://maven.shedaniel.me/" }
maven { url "https://maven.ryanliptak.com/" } // AppleSkin
maven { url "https://api.modrinth.com/maven" } // Iris
}

dependencies {
Expand All @@ -62,6 +65,13 @@ dependencies {
modCompileOnly("squeek.appleskin:appleskin-fabric:${project.apple_skin}:api") {
transitive = false
}

testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
modTestImplementation "maven.modrinth:iris:${project.iris_version}"
}

test {
useJUnitPlatform()
}

processResources {
Expand Down
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ org.gradle.jvmargs=-Xmx2048m
fabric_version=0.69.1+1.19.3
modmenu_version=5.0.2
cloth_config_version=9.0.94
apple_skin=mc1.19.3-2.4.2
apple_skin=mc1.19.3-2.4.2
iris_version=1.5.1+1.19.3
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.gson.Gson;
import me.juancarloscp52.bedrockify.Bedrockify;
import me.juancarloscp52.bedrockify.client.features.bedrockShading.BedrockBlockShading;
import me.juancarloscp52.bedrockify.client.features.bedrockShading.BedrockSunGlareShading;
import me.juancarloscp52.bedrockify.client.features.fishingBobber.FishingBobber3DModel;
import me.juancarloscp52.bedrockify.client.features.heldItemTooltips.HeldItemTooltips;
import me.juancarloscp52.bedrockify.client.features.hudOpacity.HudOpacity;
Expand Down Expand Up @@ -45,6 +46,7 @@ public class BedrockifyClient implements ClientModInitializer {
public SettingsGUI settingsGUI;
public WorldColorNoiseSampler worldColorNoiseSampler;
public BedrockBlockShading bedrockBlockShading;
public BedrockSunGlareShading bedrockSunGlareShading;
public HudOpacity hudOpacity;
public long deltaTime = 0;
private int timeFlying = 0;
Expand All @@ -66,6 +68,7 @@ public void onInitializeClient() {
settingsGUI=new SettingsGUI();
worldColorNoiseSampler = new WorldColorNoiseSampler();
bedrockBlockShading = new BedrockBlockShading();
bedrockSunGlareShading = new BedrockSunGlareShading();
hudOpacity = new HudOpacity();
keyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding("bedrockIfy.key.settings", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_B, "BedrockIfy"));

Expand Down Expand Up @@ -99,6 +102,7 @@ public void onInitializeClient() {
client.setScreen(settingsGUI.getConfigScreen(client.currentScreen,true));
}
hudOpacity.tick();
bedrockSunGlareShading.tick(client.getTickDelta());

// Stop flying drift
if(settings.disableFlyingMomentum && null != client.player && client.player.getAbilities().flying){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class BedrockifyClientSettings {
public boolean elytraStop = true;
public boolean pickupAnimations = true;
public boolean fishingBobber3D = true;
public int sunlightIntensity = 50;

public boolean isPickupAnimationsEnabled() {
return pickupAnimations;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
package me.juancarloscp52.bedrockify.client.features.bedrockShading;

import com.google.common.collect.Maps;
import me.juancarloscp52.bedrockify.Bedrockify;
import me.juancarloscp52.bedrockify.client.BedrockifyClient;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.util.Util;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.NotNull;
import org.joml.Math;
import org.joml.Vector3f;

import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* Helper class for the sun glare and the sky color like Bedrock.<br>
* Compatible with Custom Shader mods.
*/
public final class BedrockSunGlareShading {
/**
* Test cases of the {@link ClassMethodHolder} specified by modID.<br>
* See also the unittest package <code>me.juancarloscp52.bedrockify.test.client.features.bedrockShading.sunGlare</code>.
*/
private static final Map<String, ClassMethodHolder> MOD_ID_CLASS_MAP = Util.make(Maps.newHashMap(), (map) -> {
map.put("iris", new ClassMethodHolder("net.coderbot.iris.Iris", "getCurrentPack", new Object[0], (pack) -> {
return ((Optional<?>) pack).isPresent();
}));
});
private static final List<ClassMethodHolder> METHOD_INVOCATION_FAILED_LIST = new ArrayList<>();

private ShaderState shaderState = ShaderState.UNSPECIFIED;
private float skyAttenuation;
private float sunAngleDiff;
private final Vector3f sunVector3f;
private final MinecraftClient client;

public BedrockSunGlareShading() {
onSunlightIntensityChanged();
this.sunVector3f = new Vector3f();
this.client = MinecraftClient.getInstance();
}

/**
* Shows the state of Custom Shader.
*
* @see ShaderState#UNSPECIFIED
* @see ShaderState#VANILLA
* @see ShaderState#EXTERNAL
* @see ShaderState#INVOCATION_FAILED
*/
private enum ShaderState {
/**
* Initial state.
*/
UNSPECIFIED,
/**
* Shader mod is not installed, or external shader is not enabled.
*/
VANILLA,
/**
* Shader mod is present and enabled.
*/
EXTERNAL,
/**
* Shader mod is present, but method invocation failed.
*/
INVOCATION_FAILED
}

/**
* The data only class that holds the name of class, method, and args of method invocation.<br>
* Only supports static method.<br>
* This class is used with package {@link java.lang.reflect}.
*/
private static class ClassMethodHolder {
public final String canonicalName;
public final String methodName;
@NotNull
public final Object[] methodArgs;
@NotNull
public final Predicate<Object> condition;

/**
* Disable feature without method invocation.
*/
public static final ClassMethodHolder CONDITION_TRUE;

static {
CONDITION_TRUE = new ClassMethodHolder("", "", null, object -> true);
}

/**
* @param cName Class canonical name.
* @param mName Method name to invoke.
* @param mArgs Method arguments to invoke.
* @param condition {@link Predicate} for the condition whether the external shader is valid. If returns <code>true</code>, Sun Glare shading will be disabled.
*/
protected ClassMethodHolder(String cName, String mName, @NotNull Object[] mArgs, @NotNull Predicate<Object> condition) {
this.canonicalName = cName;
this.methodName = mName;
this.methodArgs = mArgs;
this.condition = condition;
}
}

public boolean shouldApplyShading() {
return this.shaderState == ShaderState.VANILLA &&
BedrockifyClient.getInstance().settings.bedrockShading &&
this.skyAttenuation < 1f;
}

public void reloadCustomShaderState() {
this.shaderState = this.fetchShaderStateInternal();
}

/**
* Determine the state of Custom Shader.
*
* @see ShaderState
*/
private ShaderState fetchShaderStateInternal() {
if (FabricLoader.getInstance().isModLoaded("optifabric")) {
// Unreachable statement.
// BedrockShading feature is disabled by BedrockIfyMixinPlugin#shouldApplyMixin if optifabric detected. How did you reach here?
return ShaderState.EXTERNAL;
}

boolean enabled = false;

for (Map.Entry<String, ClassMethodHolder> entry : MOD_ID_CLASS_MAP.entrySet()) {
final String modId = entry.getKey();
if (!FabricLoader.getInstance().isModLoaded(modId)) {
// The class probably absent because the mod has not been loaded.
continue;
}

final ClassMethodHolder holder = entry.getValue();
// Check invalid state.
if (METHOD_INVOCATION_FAILED_LIST.contains(holder)) {
return ShaderState.INVOCATION_FAILED;
}

if (holder.canonicalName.isEmpty() || holder.methodName.isEmpty() || holder.methodArgs == null) {
enabled |= holder.condition.test(null);
continue;
}

try {
// Get the class.
final Class<?> clazz = Class.forName(holder.canonicalName);

// Get the method.
final Method invoker = clazz.getMethod(holder.methodName, Arrays.stream(holder.methodArgs).map(Object::getClass).toArray(Class[]::new));
invoker.setAccessible(true);

// Execute invocation and store the result.
enabled |= holder.condition.test(invoker.invoke(clazz, holder.methodArgs));
} catch (Throwable ex) {
// Method invocation failed.
if (!METHOD_INVOCATION_FAILED_LIST.contains(holder)) {
METHOD_INVOCATION_FAILED_LIST.add(holder);

// Output the log only once.
final String message = String.format("[%s] method invocation failed into \"%s::%s(%s)\", provided by the mod \"%s\".",
Bedrockify.class.getSimpleName(),
holder.canonicalName,
holder.methodName,
Arrays.stream(holder.methodArgs).map(arg -> arg.getClass().getSimpleName()).collect(Collectors.joining(", ")),
modId
);
BedrockifyClient.LOGGER.error(message, ex);
}

// Output the log only once.
if (shaderState != ShaderState.INVOCATION_FAILED) {
BedrockifyClient.LOGGER.warn("[{}] Shader mod is present, but cannot determine the shader state. BedrockIfy Sun Glare Shading is now disabled.", Bedrockify.class.getSimpleName());
}

return ShaderState.INVOCATION_FAILED;
}
} // End of the MOD_ID_CLASS_MAP for-each loop.

// Output the log only once.
if (enabled && shaderState != ShaderState.EXTERNAL) {
BedrockifyClient.LOGGER.info("[{}] The condition matches. BedrockIfy Sun Glare Shading is now disabled.", Bedrockify.class.getSimpleName());
}
return (enabled) ? ShaderState.EXTERNAL : ShaderState.VANILLA;
}

/**
* Tick event callback to calculate the sun vector.
*
* @param tickDelta TickDelta to determine the SkyAngle.
*/
public void tick(float tickDelta) {
if (!this.shouldApplyShading()) {
return;
}

if (this.client == null || this.client.world == null) {
return;
}

if (this.client.isPaused()) {
return;
}

final float skyAngleRadian = (float) (this.client.world.getSkyAngle(tickDelta) * 2f * Math.PI);
this.sunVector3f.set(new Vector3f(-Math.sin(skyAngleRadian), Math.cos(skyAngleRadian), 0).normalize());
}

/**
* Helper method that updates the angle difference between Camera and Sun.<br>
* The result will be stored to {@link BedrockSunGlareShading#sunAngleDiff};
* a dot product of camera vector and sun vector including some factors, clamped between <code>0.0 - 1.0</code>.
*
* @see BedrockSunGlareShading#getSunAngleDiff
*/
public void updateAngleDiff() {
final float clampMax = 1f;
final float clampMin = 0f;

if (this.client == null || this.client.world == null || this.client.gameRenderer == null || !this.shouldApplyShading()) {
this.sunAngleDiff = clampMax;
return;
}

if (this.client.isPaused()) {
return;
}

final float sunSetRiseFactor = (this.sunVector3f.y < 0) ? this.sunVector3f.y * -5f : 0;
final Camera camera = this.client.gameRenderer.getCamera();
final Vector3f cameraVec3f = new Vector3f(0, 0, 1).rotate(camera.getRotation()).normalize();

this.sunAngleDiff = Math.clamp(clampMin, clampMax, (Math.safeAcos(cameraVec3f.dot(this.sunVector3f)) - 0.15f) * 2.f + sunSetRiseFactor);
}

public float getSunAngleDiff() {
return this.sunAngleDiff;
}

public float getSkyAttenuation() {
return this.skyAttenuation;
}

public void onSunlightIntensityChanged() {
this.skyAttenuation = MathHelper.clampedLerp(1f, 0.35f, BedrockifyClient.getInstance().settings.sunlightIntensity / 100f);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,14 @@ public Screen getConfigScreen(Screen parent, boolean isTransparent){
guiImprovements.add(entryBuilder.startBooleanToggle(Text.translatable("bedrockify.options.eatingAnimations"), settingsClient.eatingAnimations).setDefaultValue(true).setSaveConsumer(newValue -> settingsClient.eatingAnimations=newValue).build());
guiImprovements.add(entryBuilder.startBooleanToggle(Text.translatable("bedrockify.options.pickupAnimations"), settingsClient.pickupAnimations).setTooltip(wrapLines(Text.translatable("bedrockify.options.pickupAnimations.tooltip"))).setDefaultValue(true).setSaveConsumer(newValue -> settingsClient.pickupAnimations=newValue).build());
guiImprovements.add(entryBuilder.startBooleanToggle(Text.translatable("bedrockify.options.loadingScreen"), settingsClient.loadingScreen).setDefaultValue(true).setSaveConsumer(newValue -> settingsClient.loadingScreen=newValue).build());
guiImprovements.add(entryBuilder.startBooleanToggle(Text.translatable("bedrockify.options.bedrockShading"), settingsClient.bedrockShading).setDefaultValue(true).setSaveConsumer(newValue -> {
guiImprovements.add(entryBuilder.startBooleanToggle(Text.translatable("bedrockify.options.bedrockShading"), settingsClient.bedrockShading).setTooltip(wrapLines(Text.translatable("bedrockify.options.bedrockShading.tooltip"))).setDefaultValue(true).setSaveConsumer(newValue -> {
settingsClient.bedrockShading=newValue;
MinecraftClient.getInstance().worldRenderer.reload();
}).build());
guiImprovements.add(entryBuilder.startIntSlider(Text.translatable("bedrockify.options.sunlightIntensity"), settingsClient.sunlightIntensity,0,100).setTooltip(wrapLines(Text.translatable("bedrockify.options.sunlightIntensity.tooltip"))).setDefaultValue(50).setSaveConsumer(newValue -> {
settingsClient.sunlightIntensity = newValue;
BedrockifyClient.getInstance().bedrockSunGlareShading.onSunlightIntensityChanged();
}).build());
general.addEntry(guiImprovements.build());
SubCategoryBuilder reachAround = entryBuilder.startSubCategory(Text.translatable("bedrockify.options.subCategory.Reach-Around"));
reachAround.add(entryBuilder.startTextDescription(Text.translatable("bedrockify.options.subCategory.Reach-Around.description")).build());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package me.juancarloscp52.bedrockify.mixin.client.features.bedrockShading;
package me.juancarloscp52.bedrockify.mixin.client.features.bedrockShading.lightBlock;

import me.juancarloscp52.bedrockify.client.BedrockifyClient;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractQuadRenderer;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package me.juancarloscp52.bedrockify.mixin.client.features.bedrockShading;
package me.juancarloscp52.bedrockify.mixin.client.features.bedrockShading.lightBlock;

import me.juancarloscp52.bedrockify.client.BedrockifyClient;
import net.minecraft.block.BlockState;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package me.juancarloscp52.bedrockify.mixin.client.features.bedrockShading;
package me.juancarloscp52.bedrockify.mixin.client.features.bedrockShading.lightBlock;

import me.juancarloscp52.bedrockify.client.BedrockifyClient;
import net.minecraft.block.BlockState;
Expand Down