Last active
May 3, 2023 00:24
-
-
Save DaEgi01/925d5613380f72a89277159836a8945c to your computer and use it in GitHub Desktop.
Cities Skylines - Shadow Settings Adjuster
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Harmony; | |
using ICities; | |
using System; | |
using System.Reflection; | |
using UnityEngine; | |
using UnityEngine.Rendering; | |
namespace ShadowSettingsAdjuster | |
{ | |
/// <summary> | |
/// Relight code ripoff to play around with all shadow settings. | |
/// </summary> | |
public class ShadowSettingsAdjusterMod : LoadingExtensionBase, IUserMod | |
{ | |
public GameObject gameObject; | |
public ShadowSettingsAdjusterLogic shadowSettingsAdjusterLogic; | |
public string Name => "Shadow Settings Adjuster"; | |
public string Description => "Tool to adjust all shadow settings."; | |
public override void OnLevelLoaded(LoadMode mode) | |
{ | |
base.OnLevelLoaded(mode); | |
OnEnabled(); | |
} | |
public override void OnLevelUnloading() | |
{ | |
base.OnLevelUnloading(); | |
if (gameObject != null) | |
{ | |
GameObject.Destroy(gameObject); | |
gameObject = null; | |
} | |
} | |
public void OnEnabled() | |
{ | |
while (true) | |
{ | |
var obj = GameObject.Find(this.Name); | |
if (!obj) break; | |
GameObject.DestroyImmediate(obj); | |
} | |
gameObject = new GameObject(this.Name); | |
shadowSettingsAdjusterLogic = gameObject.AddComponent<ShadowSettingsAdjusterLogic>(); | |
} | |
public void OnDisabled() | |
{ | |
while (true) | |
{ | |
var obj = GameObject.Find(this.Name); | |
if (!obj) break; | |
GameObject.DestroyImmediate(obj); | |
} | |
} | |
} | |
public enum ShadowDistanceType | |
{ | |
@default = 0, | |
@fixed = 1, | |
} | |
public class LightShadowValues | |
{ | |
public LightShadows type; | |
public float strength; | |
public LightShadowResolution resolution; | |
public float bias; | |
public float normalBias; | |
public float nearPlane; | |
} | |
public class QualityShadowValues | |
{ | |
public ShadowQuality shadows; | |
public ShadowResolution resolution; | |
public ShadowProjection projection; | |
public ShadowDistanceType distanceType; | |
public float distanceFactor; //use a factor instead of a fixed value, since the value is based on camera view distance etc. | |
public float distanceFixed; //for tests with a fixed value. | |
public float nearPlaneOffset; | |
public int cascades; | |
public float globalShadowCascade2Split; | |
public Vector3 globalShadowCascade4Split; | |
} | |
public class ShadowSettingsAdjusterLogic : MonoBehaviour | |
{ | |
private Rect windowRect = new Rect(200, 200, 460, 600); | |
private int instanceId; | |
private bool showUI = false; | |
private LightShadowValues defaultLightShadowValues; | |
private LightShadowValues lightShadowValues; | |
private QualityShadowValues defaultQualityShadowValues; | |
private QualityShadowValues qualityShadowValues; | |
void Start() | |
{ | |
instanceId = this.GetInstanceID(); | |
var mainLight = RenderManager.instance.MainLight; | |
this.defaultLightShadowValues = new LightShadowValues() | |
{ | |
type = mainLight.shadows, | |
strength = mainLight.shadowStrength, | |
resolution = mainLight.shadowResolution, | |
bias = mainLight.shadowBias, | |
normalBias = mainLight.shadowNormalBias, | |
nearPlane = mainLight.shadowNearPlane | |
}; | |
this.lightShadowValues = new LightShadowValues() | |
{ | |
type = defaultLightShadowValues.type, | |
strength = defaultLightShadowValues.strength, | |
resolution = defaultLightShadowValues.resolution, | |
bias = defaultLightShadowValues.bias, | |
normalBias = defaultLightShadowValues.normalBias, | |
nearPlane = defaultLightShadowValues.nearPlane | |
}; | |
this.defaultQualityShadowValues = new QualityShadowValues() | |
{ | |
shadows = QualitySettings.shadows, | |
resolution = QualitySettings.shadowResolution, | |
projection = QualitySettings.shadowProjection, | |
distanceType = ShadowDistanceType.@default, | |
distanceFactor = 1f, | |
distanceFixed = QualitySettings.shadowDistance, | |
nearPlaneOffset = QualitySettings.shadowNearPlaneOffset, | |
cascades = QualitySettings.shadowCascades, | |
globalShadowCascade2Split = QualitySettings.shadowCascade2Split, | |
globalShadowCascade4Split = QualitySettings.shadowCascade4Split | |
}; | |
this.qualityShadowValues = new QualityShadowValues() | |
{ | |
shadows = defaultQualityShadowValues.shadows, | |
resolution = defaultQualityShadowValues.resolution, | |
projection = defaultQualityShadowValues.projection, | |
distanceType = defaultQualityShadowValues.distanceType, | |
distanceFactor = defaultQualityShadowValues.distanceFactor, | |
distanceFixed = defaultQualityShadowValues.distanceFixed, | |
nearPlaneOffset = defaultQualityShadowValues.nearPlaneOffset, | |
cascades = defaultQualityShadowValues.cascades, | |
globalShadowCascade2Split = defaultQualityShadowValues.globalShadowCascade2Split, | |
globalShadowCascade4Split = defaultQualityShadowValues.globalShadowCascade4Split | |
}; | |
var harmony = HarmonyInstance.Create("egi.citiesskylinesmods.shadowsettingsadjuster"); | |
ApplyHarmonyPatches(harmony); | |
} | |
void Update() | |
{ | |
if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.LeftAlt) && Input.GetKeyDown(KeyCode.S)) | |
{ | |
showUI = !showUI; | |
} | |
} | |
void OnGUI() | |
{ | |
if (showUI) | |
windowRect = GUI.Window(instanceId, windowRect, DrawWindow, "Shadow Settings Adjuster"); | |
} | |
void DrawWindow(int id) | |
{ | |
GUI.DragWindow(new Rect(0, 0, 460, 20)); | |
if (GUI.Button(new Rect(430, 20, 25, 20), "x")) | |
showUI = false; | |
#region Directional Light - Shadow Settings | |
var positionY = 0; | |
AddHeader("Directional Light", ref positionY); | |
AddOption("Type", ref positionY, ref this.lightShadowValues.type, ref this.defaultLightShadowValues.type, x => (LightShadows)(int)x, x => (float)x, 0f, 2f, 1f); | |
AddOption("Strength", ref positionY, ref this.lightShadowValues.strength, ref this.defaultLightShadowValues.strength, x => x, x => x, 0f, 1f, 0.01f); | |
AddOption("Resolution", ref positionY, ref this.lightShadowValues.resolution, ref this.defaultLightShadowValues.resolution, x => (LightShadowResolution)(int)x, x => (float)x, -1f, 3f, 1f); | |
AddOption("Bias", ref positionY, ref this.lightShadowValues.bias, ref this.defaultLightShadowValues.bias, x => x, x => x, 0f, 2f, 0.01f); | |
AddOption("Normal bias", ref positionY, ref this.lightShadowValues.normalBias, ref this.defaultLightShadowValues.normalBias, x => x, x => x, 0f, 3f, 0.01f); | |
AddOption("Near plane", ref positionY, ref this.lightShadowValues.nearPlane, ref this.defaultLightShadowValues.nearPlane, x => x, x => x, 0.1f, 10f, 0.1f); | |
#endregion | |
#region Project - Quality Settings | |
AddHeader("Quality Settings", ref positionY); | |
AddOption("Shadows", ref positionY, ref this.qualityShadowValues.shadows, ref this.defaultQualityShadowValues.shadows, x => (ShadowQuality)(int)x, x => (float)x, 0f, 2f, 1f); | |
AddOption("Resolution", ref positionY, ref this.qualityShadowValues.resolution, ref this.defaultQualityShadowValues.resolution, x => (ShadowResolution)(int)x, x => (float)x, 0f, 3f, 1f); | |
AddOption("Projection", ref positionY, ref this.qualityShadowValues.projection, ref this.defaultQualityShadowValues.projection, x => (ShadowProjection)(int)x, x => (float)x, 0f, 1f, 1f); | |
AddOption("Distance type", ref positionY, ref this.qualityShadowValues.distanceType, ref this.defaultQualityShadowValues.distanceType, x => (ShadowDistanceType)(int)x, x => (float)x, 0f, 1f, 1f); | |
if (this.qualityShadowValues.distanceType == ShadowDistanceType.@default) | |
{ | |
AddOption("Distance (factor)", ref positionY, ref this.qualityShadowValues.distanceFactor, ref this.defaultQualityShadowValues.distanceFactor, x => x, x => x, 0.125f, 4.0f, 0.125f); | |
} | |
else if (this.qualityShadowValues.distanceType == ShadowDistanceType.@fixed) | |
{ | |
AddOption("Distance (fixed)", ref positionY, ref this.qualityShadowValues.distanceFixed, ref this.defaultQualityShadowValues.distanceFixed, x => x, x => x, 0f, 10000.0f, 10f); | |
} | |
AddOption("Near plane offset", ref positionY, ref this.qualityShadowValues.nearPlaneOffset, ref this.defaultQualityShadowValues.nearPlaneOffset, x => x, x => x, 0f, 100000, 1f); | |
AddOption("Cascades", ref positionY, ref this.qualityShadowValues.cascades, ref this.defaultQualityShadowValues.cascades, x => (int)x, x => x, 0f, 4f, 2f); | |
if (this.qualityShadowValues.cascades == 2) | |
{ | |
AddOption("1 <|> 2", ref positionY, ref this.qualityShadowValues.globalShadowCascade2Split, ref this.defaultQualityShadowValues.globalShadowCascade2Split, x => x, x => x, 0f, 1f, 0.01f); | |
} | |
else if (this.qualityShadowValues.cascades == 4) | |
{ | |
GUI.Label(new Rect(5, positionY, 115, 30), "1 <|> 2"); | |
var casc4split12 = Mathf.Round(GUI.HorizontalSlider(new Rect(120, positionY + 5, 270, 25), this.qualityShadowValues.globalShadowCascade4Split.x, 0f, 1f) / 0.01f) * 0.01f; | |
if (GUI.Button(new Rect(395, positionY, 50, 30), this.qualityShadowValues.globalShadowCascade4Split.x.ToString(), GUI.skin.label)) | |
casc4split12 = this.defaultQualityShadowValues.globalShadowCascade4Split.x; | |
positionY += 30; | |
GUI.Label(new Rect(5, positionY, 115, 30), "2 <|> 3"); | |
var casc4split23 = Mathf.Round(GUI.HorizontalSlider(new Rect(120, positionY + 5, 270, 25), this.qualityShadowValues.globalShadowCascade4Split.y, 0f, 1f) / 0.01f) * 0.01f; | |
if (GUI.Button(new Rect(395, positionY, 50, 30), this.qualityShadowValues.globalShadowCascade4Split.y.ToString(), GUI.skin.label)) | |
casc4split23 = this.defaultQualityShadowValues.globalShadowCascade4Split.y; | |
positionY += 30; | |
GUI.Label(new Rect(5, positionY, 115, 30), "3 <|> 4"); | |
var casc4split34 = Mathf.Round(GUI.HorizontalSlider(new Rect(120, positionY + 5, 270, 25), this.qualityShadowValues.globalShadowCascade4Split.z, 0f, 1f) / 0.01f) * 0.01f; | |
if (GUI.Button(new Rect(395, positionY, 50, 30), this.qualityShadowValues.globalShadowCascade4Split.z.ToString(), GUI.skin.label)) | |
casc4split34 = this.defaultQualityShadowValues.globalShadowCascade4Split.z; | |
positionY += 30; | |
this.qualityShadowValues.globalShadowCascade4Split = CalculateGlobalShadowCascade4Split(casc4split12, casc4split23, casc4split34); | |
} | |
#endregion | |
} | |
private void AddHeader(string name, ref int positionY) | |
{ | |
positionY += 30; | |
GUI.Label(new Rect(5, positionY, 115, 30), name); | |
positionY += 30; | |
} | |
private void AddOption<T>(string name, ref int positionY, ref T value, ref T defaultValue, Func<float, T> toFieldConverter, Func<T, float> toFloatConverter, float minValue, float maxValue, float stepSize) | |
{ | |
GUI.Label(new Rect(5, positionY, 115, 30), name); | |
value = toFieldConverter(Mathf.Round(GUI.HorizontalSlider(new Rect(120, positionY + 5, 270, 25), toFloatConverter(value), minValue, maxValue) / stepSize) * stepSize); | |
if (GUI.Button(new Rect(395, positionY, 50, 30), value.ToString(), GUI.skin.label)) | |
value = defaultValue; | |
positionY += 30; | |
} | |
private Vector3 CalculateGlobalShadowCascade4Split(float split12, float split23, float split34) | |
{ | |
if (split23 > split34) | |
split23 = split34; | |
if (split12 > split23) | |
split12 = split23; | |
return new Vector3(split12, split23, split34); | |
} | |
private void ApplyHarmonyPatches(HarmonyInstance harmony) | |
{ | |
var updateLighting = typeof(DayNightProperties).GetMethod("UpdateLighting", BindingFlags.Instance | BindingFlags.NonPublic); | |
var updateLightingPostfix = typeof(ShadowSettingsAdjusterLogic).GetMethod(nameof(UpdateLightingPostfix), BindingFlags.Static | BindingFlags.NonPublic); | |
harmony.Patch(updateLighting, null, new HarmonyMethod(updateLightingPostfix)); | |
var updateTransform = typeof(CameraController).GetMethod("UpdateTransform", BindingFlags.Instance | BindingFlags.NonPublic); | |
var updateTransformPostfix = typeof(ShadowSettingsAdjusterLogic).GetMethod(nameof(UpdateTransformPostfix), BindingFlags.Static | BindingFlags.NonPublic); | |
harmony.Patch(updateTransform, null, new HarmonyMethod(updateTransformPostfix)); | |
var lateUpdate = typeof(CameraController).GetMethod("LateUpdate", BindingFlags.Instance | BindingFlags.NonPublic); | |
var lateUpdatePostfix = typeof(ShadowSettingsAdjusterLogic).GetMethod(nameof(LateUpdatePostfix), BindingFlags.Static | BindingFlags.NonPublic); | |
harmony.Patch(lateUpdate, null, new HarmonyMethod(lateUpdatePostfix)); | |
} | |
private static void UpdateLightingPostfix(DayNightProperties __instance) | |
{ | |
var mainLight = RenderManager.instance.MainLight; | |
var gameObject = GameObject.Find("Shadow Settings Adjuster"); | |
var shadowSettingsAdjusterLogic = gameObject.GetComponent<ShadowSettingsAdjusterLogic>(); | |
//adjusted in CameraController.LateUpdate | |
//mainLight.shadows = shadowSettingsAdjusterLogic.lightShadowValues.type; | |
mainLight.shadowStrength = shadowSettingsAdjusterLogic.lightShadowValues.strength; | |
mainLight.shadowResolution = shadowSettingsAdjusterLogic.lightShadowValues.resolution; | |
mainLight.shadowBias = shadowSettingsAdjusterLogic.lightShadowValues.bias; | |
mainLight.shadowNormalBias = shadowSettingsAdjusterLogic.lightShadowValues.normalBias; | |
mainLight.shadowNearPlane = shadowSettingsAdjusterLogic.lightShadowValues.nearPlane; | |
QualitySettings.shadows = shadowSettingsAdjusterLogic.qualityShadowValues.shadows; | |
QualitySettings.shadowResolution = shadowSettingsAdjusterLogic.qualityShadowValues.resolution; | |
QualitySettings.shadowProjection = shadowSettingsAdjusterLogic.qualityShadowValues.projection; | |
//adjusted in CameraController.UpdateTransform | |
//QualitySettings.shadowDistance = shadowSettingsAdjusterLogic.qualityShadowValues.distance; | |
QualitySettings.shadowNearPlaneOffset = shadowSettingsAdjusterLogic.qualityShadowValues.nearPlaneOffset; | |
QualitySettings.shadowCascades = shadowSettingsAdjusterLogic.qualityShadowValues.cascades; | |
QualitySettings.shadowCascade2Split = shadowSettingsAdjusterLogic.qualityShadowValues.globalShadowCascade2Split; | |
QualitySettings.shadowCascade4Split = shadowSettingsAdjusterLogic.qualityShadowValues.globalShadowCascade4Split; | |
} | |
private static void UpdateTransformPostfix(CameraController __instance) | |
{ | |
var gameObject = GameObject.Find("Shadow Settings Adjuster"); | |
var shadowSettingsAdjusterLogic = gameObject.GetComponent<ShadowSettingsAdjusterLogic>(); | |
if (shadowSettingsAdjusterLogic.qualityShadowValues.distanceType == ShadowDistanceType.@default) | |
{ | |
QualitySettings.shadowDistance = QualitySettings.shadowDistance * shadowSettingsAdjusterLogic.qualityShadowValues.distanceFactor; | |
} | |
else | |
{ | |
QualitySettings.shadowDistance = shadowSettingsAdjusterLogic.qualityShadowValues.distanceFixed; | |
} | |
} | |
private static void LateUpdatePostfix(CameraController __instance) | |
{ | |
var mainLight = RenderManager.instance.MainLight; | |
var gameObject = GameObject.Find("Shadow Settings Adjuster"); | |
var shadowSettingsAdjusterLogic = gameObject.GetComponent<ShadowSettingsAdjusterLogic>(); | |
mainLight.shadows = shadowSettingsAdjusterLogic.lightShadowValues.type; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment