Skip to content

Instantly share code, notes, and snippets.

@ronyx69
Last active July 28, 2023 01:56
Show Gist options
  • Save ronyx69/a75400389e7561164bacabadf0095a2b to your computer and use it in GitHub Desktop.
Save ronyx69/a75400389e7561164bacabadf0095a2b to your computer and use it in GitHub Desktop.
Source code for Relight mod. Rebalanced and customizable lighting and tonemapping. UI / saving and preset system by Simon Royer. Shadow bias change by saki7. Shadow smoothing change by TPB.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICities;
using UnityEngine;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Xml.Serialization;
using System.IO;
using ColossalFramework.PlatformServices;
using ColossalFramework.IO;
using Harmony;
namespace LightingRebalance
{
public class LightingRebalanceMod : LoadingExtensionBase, IUserMod
{
private GameObject gameobj;
private static LightingRebalanceLogic logic;
public string Name { get { return "Relight"; } }
public string Description { get { return "Rebalanced and customizable lighting and tonemapping."; } }
public override void OnLevelLoaded(LoadMode mode)
{
base.OnLevelLoaded(mode);
OnEnabled();
}
public override void OnLevelUnloading()
{
base.OnLevelUnloading();
if (gameobj != null)
{
UnityEngine.Object.Destroy(gameobj);
gameobj = null;
logic = null;
}
}
private HarmonyInstance harmony;
public void OnEnabled()
{
while (true)
{
var obj = GameObject.Find("Relight");
if (!obj) break;
GameObject.DestroyImmediate(obj);
}
gameobj = new GameObject("Relight");
logic = gameobj.AddComponent<LightingRebalanceLogic>();
harmony = HarmonyInstance.Create("com.ronyx.relight.biaspatch");
harmony.PatchAll(Assembly.GetExecutingAssembly());
}
public void OnDisabled()
{
while (true)
{
var obj = GameObject.Find("Relight");
if (!obj) break;
GameObject.DestroyImmediate(obj);
}
harmony = null;
}
public class Raycaster : ToolBase
{
public static float RayDistance(float angle, Vector3 transform)
{
Vector3 direction = Quaternion.AngleAxis(angle, transform) * Camera.main.transform.forward;
//Vector3 direction = Camera.main.transform.forward;
ToolBase.RaycastInput input = new ToolBase.RaycastInput(new Ray(Camera.main.transform.position, direction), Camera.main.farClipPlane);
input.m_ignoreBuildingFlags = Building.Flags.None;
input.m_ignoreNodeFlags = NetNode.Flags.None;
input.m_ignoreSegmentFlags = NetSegment.Flags.None;
input.m_ignorePropFlags = PropInstance.Flags.None;
input.m_buildingService = new RaycastService(ItemClass.Service.None, ItemClass.SubService.None, ItemClass.Layer.Default);
input.m_netService = new RaycastService(ItemClass.Service.None, ItemClass.SubService.None, ItemClass.Layer.Default);
input.m_netService2 = new RaycastService(ItemClass.Service.None, ItemClass.SubService.None, ItemClass.Layer.Default);
input.m_propService = new RaycastService(ItemClass.Service.None, ItemClass.SubService.None, ItemClass.Layer.Default);
ToolBase.RaycastOutput rayOutput;
if (ToolBase.RayCast(input, out rayOutput)) return Vector3.Distance(Camera.main.transform.position, rayOutput.m_hitPos);
else return 1000f;
}
}
public static float GetShadowBias()
{
var rayDistance = Raycaster.RayDistance(0, Camera.main.transform.forward);
var rayDistanceMax = rayDistance;
rayDistance = Raycaster.RayDistance(10, Camera.main.transform.right);
if (rayDistance > rayDistanceMax) rayDistanceMax = rayDistance;
rayDistance = Raycaster.RayDistance(-10, Camera.main.transform.right);
if (rayDistance > rayDistanceMax) rayDistanceMax = rayDistance;
rayDistance = Raycaster.RayDistance(10, Camera.main.transform.up);
if (rayDistance > rayDistanceMax) rayDistanceMax = rayDistance;
rayDistance = Raycaster.RayDistance(20, Camera.main.transform.up);
if (rayDistance > rayDistanceMax) rayDistanceMax = rayDistance;
rayDistance = Raycaster.RayDistance(-10, Camera.main.transform.up);
if (rayDistance > rayDistanceMax) rayDistanceMax = rayDistance;
rayDistance = Raycaster.RayDistance(-20, Camera.main.transform.up);
if (rayDistance > rayDistanceMax) rayDistanceMax = rayDistance;
rayDistanceMax = Mathf.Clamp(rayDistanceMax, 0f, 1000f);
if (rayDistanceMax == 0) rayDistanceMax = 1000;
var heightMultiplier = 0.6f; // multiplier - how much height affects bias
var heightUpper = 10f; // divider - affects height above which shadow bias is no longer relevant
var heightLower = 20f; // subtracter - affects height below which shadow bias is no longer relevant
var heightAngleRatio = 0.65f; // multiplier (0-1) - higher values means angle affects shadow bias more at increased height
var angleBaseAffect = 0.15f; // lower limit for how much angle affects bias
var rayAffect = 16; // divider (1/value) - how much raycasting is mixed into the height value
var cameraController = ToolsModifierControl.cameraController;
// calculates relevant height by subtracting terrain height from camera height
var height = cameraController.transform.position.y - cameraController.m_currentHeight;
if (rayDistanceMax > height) height = ((rayAffect - 1) * height + rayDistanceMax) / rayAffect;
// calculates camera angle: 0 = at horizon, 90 = down
var angle = cameraController.m_targetAngle.y; if (angle > 90f || angle < 0f) angle = 0;
// calculates relevant angle, where we're looking: 0 = down, 1 = at horizon
var calc_angle = 1 - (angle / 90f);
// calculates relevant height: 0 = close to ground, 1 = very high
var calc_height = Mathf.Clamp(((height - heightLower) * heightMultiplier / heightUpper), 0f, 100f) / 100;
// calculates bias, increasing height and angle increases leads to higher values
var calc_bias = calc_height + (((calc_height * heightAngleRatio) + angleBaseAffect) * calc_angle);
// makes bias increase more quickly when going above 0.2
var exp_bias = Math.Pow((calc_bias + 0.8f), 1.5f) - 0.8f;
// clamps bias to lowest and highest useful values
var final_bias = Mathf.Clamp(Convert.ToSingle(exp_bias), 0.1f, 1f);
if (LightingRebalanceLogic.forceLowBias) return Mathf.Clamp(final_bias, 0.20f, 1f) - 0.19f;
return final_bias;
}
}
public class LightingRebalanceLogic : MonoBehaviour
{
// SIMON PART (UI + SAVING)
public bool ShowUI = false;
private Rect windowRect = new Rect(200, 200, 450, 581);
private int instanceID, windowMode = 0;
private Vector2 scrollPresets = Vector2.zero;
private string nameTextfield = "Preset Name";
// lighting values
public float[] lightingValues, oldLightingValues;
public bool skyTonemappingUi, oldSkyTonemappingUi;
// shadows values
public bool disableSmoothing, oldDisableSmoothing;
public static bool forceLowBias, oldForceLowBias;
public string CachePath = DataLocation.localApplicationData + "\\RelightCache.light";
void Start()
{
instanceID = this.GetInstanceID();
LightPreset.LoadLightPresets();
if (File.Exists(CachePath))
{
LoadCache();
CalculateAll(lightingValues, skyTonemappingUi);
ApplyShadowValues();
}
else
AllToZero();
}
void Update()
{
if (Input.GetKey(KeyCode.LeftShift))
{
if (Input.GetKey(KeyCode.LeftAlt))
{
if (Input.GetKeyDown(KeyCode.L))
{
ShowUI = !ShowUI;
}
}
}
if (ShowUI)
{
if (IsAnyDifference())
{
CalculateAll(lightingValues, skyTonemappingUi);
ApplyShadowValues();
SaveCache();
}
oldLightingValues = new float[]
{
lightingValues[0], lightingValues[1], lightingValues[2], lightingValues[3], lightingValues[4], lightingValues[5], lightingValues[6], lightingValues[7], lightingValues[8], lightingValues[9], lightingValues[10], lightingValues[11], lightingValues[12]
};
oldSkyTonemappingUi = skyTonemappingUi;
oldDisableSmoothing = disableSmoothing;
oldForceLowBias = forceLowBias;
}
}
void OnGUI()
{
if (ShowUI)
windowRect = GUI.Window(instanceID, windowRect, DrawWindow, "Relight");
}
void DrawWindow(int id)
{
GUI.DragWindow(new Rect(0, 0, 420, 20));
if (GUI.Button(new Rect(422, 4, 25, 20), "x"))
ShowUI = false;
windowMode = GUI.Toolbar(new Rect(5, 26, 440, 25), windowMode, new string[] { "Lighting", "Presets" });
if (windowMode == 0)
{
// CUSTOMIZATION WINDOW MODE
GUI.Label(new Rect(180, 60, 150, 26), "<size=14>Tonemapping</size>");
// brightness
GUI.Label(new Rect(5, 80, 115, 30), "Brightness");
lightingValues[10] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 85, 270, 25), lightingValues[10], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 80, 50, 30), lightingValues[10].ToString(), GUI.skin.label))
lightingValues[10] = 0;
// gamma
GUI.Label(new Rect(5, 110, 115, 30), "Gamma");
lightingValues[11] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 115, 270, 25), lightingValues[11], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 110, 50, 30), lightingValues[11].ToString(), GUI.skin.label))
lightingValues[11] = 0;
// contrast
GUI.Label(new Rect(5, 140, 115, 30), "Contrast");
lightingValues[12] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 145, 270, 25), lightingValues[12], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 140, 50, 30), lightingValues[12].ToString(), GUI.skin.label))
lightingValues[12] = 0;
GUI.Label(new Rect(200, 160, 150, 26), "<size=14>Lighting</size>");
// temperature
GUI.Label(new Rect(5, 190, 115, 30), "Temperature");
lightingValues[0] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 195, 270, 25), lightingValues[0], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 190, 50, 30), lightingValues[0].ToString(), GUI.skin.label))
lightingValues[0] = 0;
// tint
GUI.Label(new Rect(5, 220, 115, 30), "Tint");
lightingValues[1] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 225, 270, 25), lightingValues[1], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 220, 50, 30), lightingValues[1].ToString(), GUI.skin.label))
lightingValues[1] = 0;
// sun temperature
GUI.Label(new Rect(5, 250, 115, 30), "Sun Temperature");
lightingValues[2] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 255, 270, 25), lightingValues[2], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 250, 50, 30), lightingValues[2].ToString(), GUI.skin.label))
lightingValues[2] = 0;
// sun tint
GUI.Label(new Rect(5, 280, 115, 30), "Sun Tint");
lightingValues[3] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 285, 270, 25), lightingValues[3], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 280, 50, 30), lightingValues[3].ToString(), GUI.skin.label))
lightingValues[3] = 0;
// sky temperature
GUI.Label(new Rect(5, 310, 115, 30), "Sky Temperature");
lightingValues[4] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 315, 270, 25), lightingValues[4], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 310, 50, 30), lightingValues[4].ToString(), GUI.skin.label))
lightingValues[4] = 0;
// sky tint
GUI.Label(new Rect(5, 340, 115, 30), "Sky Tint");
lightingValues[5] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 345, 270, 25), lightingValues[5], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 340, 50, 30), lightingValues[5].ToString(), GUI.skin.label))
lightingValues[5] = 0;
// moon temperature
GUI.Label(new Rect(5, 370, 115, 30), "Moon Temperature");
lightingValues[6] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 375, 270, 25), lightingValues[6], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 370, 50, 30), lightingValues[6].ToString(), GUI.skin.label))
lightingValues[6] = 0;
// moon tint
GUI.Label(new Rect(5, 400, 115, 30), "Moon Tint");
lightingValues[7] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 405, 270, 25), lightingValues[7], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 400, 50, 30), lightingValues[7].ToString(), GUI.skin.label))
lightingValues[7] = 0;
// moonlight
GUI.Label(new Rect(5, 430, 115, 30), "Moon Light");
lightingValues[8] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 435, 270, 25), lightingValues[8], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 430, 50, 30), lightingValues[8].ToString(), GUI.skin.label))
lightingValues[8] = 0;
// Twilight tint
GUI.Label(new Rect(5, 460, 115, 30), "Twilight Tint");
lightingValues[9] = (float)(Math.Round(GUI.HorizontalSlider(new Rect(120, 465, 270, 25), lightingValues[9], -1f, 1f) * 20) / 20);
if (GUI.Button(new Rect(395, 460, 50, 30), lightingValues[9].ToString(), GUI.skin.label))
lightingValues[9] = 0;
skyTonemappingUi = GUI.Toggle(new Rect(5, 495, 348, 28), skyTonemappingUi, " Enable Sky Tonemapping");
disableSmoothing = GUI.Toggle(new Rect(5, 523, 348, 28), disableSmoothing, " Disable Shadow Smoothing");
forceLowBias = GUI.Toggle(new Rect(5, 551, 348, 28), forceLowBias, " Force Low Shadow Bias");
if (GUI.Button(new Rect(355, 516, 88, 28), "Reset All"))
AllToZero();
}
else if (windowMode == 1)
{
// PRESETS WINDOW MODE
if (LightPreset.LightPresets.Count > 0)
{
scrollPresets = GUI.BeginScrollView(new Rect(5, 55, 440, 200), scrollPresets, new Rect(0, 0, 415, LightPreset.LightPresets.Count * 30));
for (int i = 0; i < LightPreset.LightPresets.Count; i++)
{
var preset = LightPreset.LightPresets[i];
GUI.Label(new Rect(5, i * 30 + 3, 240, 25), preset.m_presetName);
if (GUI.Button(new Rect(245, i * 30, 70, 28), "Load"))
LoadPreset(preset);
if (preset.m_canBeDeleted)
{
if (GUI.Button(new Rect(320, i * 30, 70, 28), "Delete"))
LightPreset.DeletePreset(preset);
}
else
GUI.Label(new Rect(320, i * 30 + 2, 70, 28), "[ <i>Workshop</i> ]");
}
GUI.EndScrollView();
}
else
{
GUI.Box(new Rect(5, 55, 440, 200), string.Empty);
GUI.Label(new Rect(28, 60, 410, 30), "No preset found !");
}
if (GUI.Button(new Rect(5, 260, 440, 27), "Reload Presets List"))
LightPreset.LoadLightPresets();
if (File.Exists(DataLocation.localApplicationData + @"\ModConfig\RelightPresets\" + LightPreset.ToFileName(nameTextfield) + ".light"))
{
GUI.color = Color.red;
nameTextfield = GUI.TextField(new Rect(5, 290, 440, 27), nameTextfield);
GUI.Label(new Rect(5, 320, 440, 27), "<b>A preset with this name already exists !</b>", GUI.skin.button);
GUI.color = Color.white;
}
else
{
nameTextfield = GUI.TextField(new Rect(5, 290, 440, 27), nameTextfield);
if (GUI.Button(new Rect(5, 320, 440, 27), "<b>+</b> Save Preset Locally"))
CreatePreset();
}
if (GUI.Button(new Rect(5, 350, 440, 27), "Open Local Presets Folder"))
{
string path = DataLocation.localApplicationData + @"\ModConfig\RelightPresets\";
if (!Directory.Exists(DataLocation.localApplicationData + @"\ModConfig\"))
Directory.CreateDirectory(DataLocation.localApplicationData + @"\ModConfig\");
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
Application.OpenURL("file://" + path);
}
}
}
public static void CalculateAll(float[] values, bool skyTonemapping)
{
CalculateLighting(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], values[9]);
CalculateTonemapping(values[10], values[11], values[12]);
SkyTonemapping(skyTonemapping);
}
private bool IsAnyDifference(/*float[] valuesNew, bool skyTonemappingNew, float[] valuesOld, bool skyTonemappingOld*/)
{
for (int i = 0; i <= 12; i++)
{
if (lightingValues[i] != oldLightingValues[i])
return true;
}
if (disableSmoothing != oldDisableSmoothing)
return true;
if (forceLowBias != oldForceLowBias)
return true;
return (skyTonemappingUi != oldSkyTonemappingUi);
}
public void SaveCache()
{
if (File.Exists(CachePath))
File.Delete(CachePath);
TextWriter tw = new StreamWriter(CachePath);
tw.WriteLine("0 = " + lightingValues[0]);
tw.WriteLine("1 = " + lightingValues[1]);
tw.WriteLine("2 = " + lightingValues[2]);
tw.WriteLine("3 = " + lightingValues[3]);
tw.WriteLine("4 = " + lightingValues[4]);
tw.WriteLine("5 = " + lightingValues[5]);
tw.WriteLine("6 = " + lightingValues[6]);
tw.WriteLine("7 = " + lightingValues[7]);
tw.WriteLine("8 = " + lightingValues[8]);
tw.WriteLine("9 = " + lightingValues[9]);
tw.WriteLine("10 = " + lightingValues[10]);
tw.WriteLine("11 = " + lightingValues[11]);
tw.WriteLine("12 = " + lightingValues[12]);
tw.WriteLine("disableSmoothing = " + disableSmoothing);
tw.WriteLine("forceLowBias = " + forceLowBias);
tw.WriteLine("skyTmpg = " + skyTonemappingUi);
tw.Close();
}
public void ApplyShadowValues()
{
if (disableSmoothing)
QualitySettings.shadows = ShadowQuality.HardOnly;
else
QualitySettings.shadows = ShadowQuality.All;
}
public void LoadCache()
{
if (!File.Exists(CachePath))
SaveCache();
string[] lines = File.ReadAllLines(CachePath);
var dict = new Dictionary<int, float>();
foreach (string line in lines.Where(s => s.Contains(" = ")))
{
string[] data = line.Split(new string[] { " = " }, StringSplitOptions.RemoveEmptyEntries);
if (data[0] == "skyTmpg")
{
skyTonemappingUi = bool.Parse(data[1]);
oldSkyTonemappingUi = skyTonemappingUi;
}
else if (data[0] == "disableSmoothing")
{
disableSmoothing = bool.Parse(data[1]);
oldDisableSmoothing = disableSmoothing;
}
else if (data[0] == "forceLowBias")
{
forceLowBias = bool.Parse(data[1]);
oldForceLowBias = forceLowBias;
}
else
{
int id = 0;
if (int.TryParse(data[0], out id))
{
dict[id] = 0;
dict[id] = float.Parse(data[1]);
}
}
}
lightingValues = new float[]
{
dict[0], dict[1], dict[2], dict[3], dict[4], dict[5], dict[6], dict[7], dict[8], dict[9], dict[10], dict[11], dict[12]
};
oldLightingValues = new float[]
{
dict[0], dict[1], dict[2], dict[3], dict[4], dict[5], dict[6], dict[7], dict[8], dict[9], dict[10], dict[11], dict[12]
};
}
public void LoadPreset(LightPreset preset)
{
lightingValues = new float[]
{
preset.m_values[0], preset.m_values[1], preset.m_values[2], preset.m_values[3], preset.m_values[4], preset.m_values[5], preset.m_values[6], preset.m_values[7], preset.m_values[8], preset.m_values[9], preset.m_values[10], preset.m_values[11], preset.m_values[12]
};
oldLightingValues = new float[]
{
preset.m_values[0], preset.m_values[1], preset.m_values[2], preset.m_values[3], preset.m_values[4], preset.m_values[5], preset.m_values[6], preset.m_values[7], preset.m_values[8], preset.m_values[9], preset.m_values[10], preset.m_values[11], preset.m_values[12]
};
skyTonemappingUi = preset.m_skyTonemapping;
oldSkyTonemappingUi = preset.m_skyTonemapping;
CalculateAll(preset.m_values, preset.m_skyTonemapping);
SaveCache();
}
public void CreatePreset()
{
LightPreset preset = new LightPreset();
preset.m_presetName = nameTextfield;
preset.m_values = new float[]
{
lightingValues[0], lightingValues[1], lightingValues[2], lightingValues[3], lightingValues[4], lightingValues[5], lightingValues[6], lightingValues[7], lightingValues[8], lightingValues[9], lightingValues[10], lightingValues[11], lightingValues[12]
};
preset.m_canBeDeleted = true;
preset.m_skyTonemapping = skyTonemappingUi;
preset.SavePreset(true);
}
private void AllToZero()
{
lightingValues = new float[]
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
oldLightingValues = new float[]
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
skyTonemappingUi = true;
oldSkyTonemappingUi = skyTonemappingUi;
SaveCache();
CalculateAll(lightingValues, skyTonemappingUi);
}
#region RONYX PART (LOGIC)
// Intensity Tweak
private static float tweak_Intensity = 1.90f;
// Time
private static float time_Sunrise1 = 0.23f;
private static float time_Sunrise2 = 0.27f;
private static float time_Early = 0.40f;
private static float time_Late = 0.60f;
private static float time_Sunset1 = 0.74f;
private static float time_Sunset2 = 0.76f;
// Placeholder Color
private static Color initcolor = new Color(1f, 1f, 1f, 1f);
// Lighting gradients, that will be edited and assigned, instead of creating new ones every time.
private static Gradient gradient_Direct = new Gradient
{
colorKeys = new GradientColorKey[] {
new GradientColorKey(initcolor, time_Sunrise1),
new GradientColorKey(initcolor, time_Sunrise2),
new GradientColorKey(initcolor, time_Early),
new GradientColorKey(initcolor, time_Late),
new GradientColorKey(initcolor, time_Sunset1),
new GradientColorKey(initcolor, time_Sunset2),
},
alphaKeys = new GradientAlphaKey[] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f) }
};
private static Gradient gradient_Sky = new Gradient
{
colorKeys = new GradientColorKey[] {
new GradientColorKey(initcolor, time_Sunrise1),
new GradientColorKey(initcolor, time_Sunrise2),
new GradientColorKey(initcolor, time_Early),
new GradientColorKey(initcolor, time_Late),
new GradientColorKey(initcolor, time_Sunset1),
new GradientColorKey(initcolor, time_Sunset2),
},
alphaKeys = new GradientAlphaKey[] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f) }
};
private static Gradient gradient_Equator = new Gradient
{
colorKeys = new GradientColorKey[] {
new GradientColorKey(initcolor, time_Sunrise1),
new GradientColorKey(initcolor, time_Sunrise2),
new GradientColorKey(initcolor, time_Early),
new GradientColorKey(initcolor, time_Late),
new GradientColorKey(initcolor, time_Sunset1),
new GradientColorKey(initcolor, time_Sunset2),
},
alphaKeys = new GradientAlphaKey[] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f) }
};
private static Gradient gradient_Ground = new Gradient
{
colorKeys = new GradientColorKey[] {
new GradientColorKey(initcolor, time_Sunrise1),
new GradientColorKey(initcolor, time_Sunrise2),
new GradientColorKey(initcolor, time_Early),
new GradientColorKey(initcolor, time_Late),
new GradientColorKey(initcolor, time_Sunset1),
new GradientColorKey(initcolor, time_Sunset2),
},
alphaKeys = new GradientAlphaKey[] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f) }
};
public static void CalculateLighting(float temperature, float tint, float sunTemp, float sunTint, float skyTemp, float skyTint, float moonTemp, float moonTint, float moonLight, float twilightTint)
{
// Day/Night Value
var mult_Day = 1.00f * tweak_Intensity;
var mult_Night = 0.32f * (1 + (0.7f * moonLight)) * tweak_Intensity;
var mult_Twilight = ((mult_Day * 2) + mult_Night) / 3f;
// Light Value
var mult_Ambient = 0.60f;
var mult_Sun = 0.25f;
var mult_Moon = 0.20f;
var mult_Sky = 0.95f;
var mult_Equator = 0.85f;
var mult_Ground = 0.65f;
// Main
var mult_Temperature = temperature;
var mult_Tint = tint;
var tint_Twilight = 0.08f * (twilightTint + 1);
var temp_Sun = sunTemp;
var temp_Moon = -moonTemp;
var temp_Ambient = -skyTemp;
var tint_Sun = sunTint;
var tint_Moon = moonTint;
var tint_Ambient = skyTint;
// Overall Temperature and Tint
var calc_tempMultR = 1 + (0.1f * mult_Temperature);
var calc_tempMultB = 1 - (0.1f * mult_Temperature);
var calc_tintMult = 1 + (0.1f * mult_Tint);
// Sun Temperature and Tint
var calc_tempOffset_Sun = (((temp_Sun + 1) * 0.5f) * 0.05f);
var calc_tintOffset_Sun = ((tint_Sun * 0.5f) * 0.05f);
// Moon Temperature and Tint
var calc_tempOffset_Moon = (((temp_Moon + 1) * 0.5f) * 0.1f);
var calc_tintOffset_Moon = ((tint_Moon * 0.5f) * 0.06f);
// Ambient Temperature and Tint
var calc_tempOffset_Sky = (((temp_Ambient + 1) * 0.5f) * 0.23f);
var calc_tempOffset_Equator = (((temp_Ambient + 1) * 0.5f) * 0.18f);
var calc_tempOffset_Ground = (((temp_Ambient + 1) * 0.5f) * 0.13f);
var calc_tintOffset_Ambient = tint_Ambient * 0.06f;
// Value Day
var calc_SunDay = mult_Sun * mult_Day;
var calc_SkyDay = mult_Sky * mult_Day * mult_Ambient;
var calc_EquatorDay = mult_Equator * mult_Day * mult_Ambient;
var calc_GroundDay = mult_Ground * mult_Day * mult_Ambient;
// Value Night
var calc_MoonNight = mult_Moon * mult_Night;
var calc_SkyNight = mult_Sky * mult_Night * mult_Ambient;
var calc_EquatorNight = mult_Equator * mult_Night * mult_Ambient;
var calc_GroundNight = mult_Ground * mult_Night * mult_Ambient;
// Value Twilight
var calc_SunTwilight = mult_Moon * mult_Twilight;
var calc_SkyTwilight = mult_Sky * mult_Twilight * mult_Ambient * 1.2f;
var calc_EquatorTwilight = mult_Equator * mult_Twilight * mult_Ambient * 1.2f;
var calc_GroundTwilight = mult_Ground * mult_Twilight * mult_Ambient * 1.2f;
// Day Colors
Color color_Direct_Day = new Color(
(calc_SunDay + (0.92f * calc_tempOffset_Sun)) * calc_tempMultR,
(calc_SunDay + calc_tintOffset_Sun) * calc_tintMult,
(calc_SunDay - calc_tempOffset_Sun) * calc_tempMultB,
1f);
Color color_Sky_Day = new Color(
(calc_SkyDay - calc_tempOffset_Sky) * calc_tempMultR,
(calc_SkyDay + calc_tintOffset_Ambient) * calc_tintMult,
(calc_SkyDay + (0.92f * calc_tempOffset_Sky)) * calc_tempMultB,
1f);
Color color_Equator_Day = new Color(
(calc_EquatorDay - calc_tempOffset_Equator) * calc_tempMultR,
(calc_EquatorDay + calc_tintOffset_Ambient) * calc_tintMult,
(calc_EquatorDay + (0.92f * calc_tempOffset_Equator)) * calc_tempMultB,
1f);
Color color_Ground_Day = new Color(
(calc_GroundDay - calc_tempOffset_Ground) * calc_tempMultR,
(calc_GroundDay + calc_tintOffset_Ambient) * calc_tintMult,
(calc_GroundDay + (0.92f * calc_tempOffset_Ground)) * calc_tempMultB,
1f);
// Night Colors
Color color_Direct_Night = new Color(
(calc_MoonNight - calc_tempOffset_Moon) * calc_tempMultR,
(calc_MoonNight + calc_tintOffset_Moon) * calc_tintMult,
(calc_MoonNight + calc_tempOffset_Moon) * calc_tempMultB,
1f);
Color color_Sky_Night = new Color(
(calc_SkyNight - calc_tempOffset_Sky) * calc_tempMultR,
(calc_SkyNight + calc_tintOffset_Ambient) * calc_tintMult,
(calc_SkyNight + (0.92f * calc_tempOffset_Sky)) * calc_tempMultB,
1f);
Color color_Equator_Night = new Color(
(calc_EquatorNight - calc_tempOffset_Equator) * calc_tempMultR,
(calc_EquatorNight + calc_tintOffset_Ambient) * calc_tintMult,
(calc_EquatorNight + (0.92f * calc_tempOffset_Equator)) * calc_tempMultB,
1f);
Color color_Ground_Night = new Color(
(calc_GroundNight - calc_tempOffset_Ground) * calc_tempMultR,
(calc_GroundNight + calc_tintOffset_Ambient) * calc_tintMult,
(calc_GroundNight + (0.92f * calc_tempOffset_Ground)) * calc_tempMultB,
1f);
// Twilight Colors
Color color_Direct_Twilight = new Color(
(calc_SunTwilight + calc_tempOffset_Sun + tint_Twilight) * calc_tempMultR,
(calc_SunTwilight + calc_tintOffset_Sun) * calc_tintMult,
(calc_SunTwilight - calc_tempOffset_Sun - tint_Twilight) * calc_tempMultB,
1f);
Color color_Sky_Twilight = new Color(
(calc_SkyTwilight - calc_tempOffset_Sky - tint_Twilight) * calc_tempMultR,
(calc_SkyTwilight + calc_tintOffset_Ambient - (0.1f * tint_Twilight)) * calc_tintMult,
(calc_SkyTwilight + calc_tempOffset_Sky + tint_Twilight) * calc_tempMultB,
1f);
Color color_Equator_Twilight = new Color(
(calc_EquatorTwilight - calc_tempOffset_Equator - tint_Twilight) * calc_tempMultR,
(calc_EquatorTwilight + calc_tintOffset_Ambient - (0.1f * tint_Twilight)) * calc_tintMult,
(calc_EquatorTwilight + calc_tempOffset_Equator + tint_Twilight) * calc_tempMultB,
1f);
Color color_Ground_Twilight = new Color(
(calc_GroundTwilight - calc_tempOffset_Ground - tint_Twilight) * calc_tempMultR,
(calc_GroundTwilight + calc_tintOffset_Ambient - (0.1f * tint_Twilight)) * calc_tintMult,
(calc_GroundTwilight + calc_tempOffset_Ground + tint_Twilight) * calc_tempMultB,
1f);
// Updating the colors for the gradients.
GradientAlphaKey[] gradientAlphaKeys = new GradientAlphaKey[] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f) };
GradientColorKey[] gradientColorKeys_Direct = new GradientColorKey[] {
new GradientColorKey(color_Direct_Night, time_Sunrise1),
new GradientColorKey(color_Direct_Twilight, time_Sunrise2),
new GradientColorKey(color_Direct_Day, time_Early),
new GradientColorKey(color_Direct_Day, time_Late),
new GradientColorKey(color_Direct_Twilight, time_Sunset1),
new GradientColorKey(color_Direct_Night, time_Sunset2), };
gradient_Direct.SetKeys(gradientColorKeys_Direct, gradientAlphaKeys);
GradientColorKey[] gradientColorKeys_Sky = new GradientColorKey[] {
new GradientColorKey(color_Sky_Night, time_Sunrise1),
new GradientColorKey(color_Sky_Twilight, time_Sunrise2),
new GradientColorKey(color_Sky_Day, time_Early),
new GradientColorKey(color_Sky_Day, time_Late),
new GradientColorKey(color_Sky_Twilight, time_Sunset1),
new GradientColorKey(color_Sky_Night, time_Sunset2), };
gradient_Sky.SetKeys(gradientColorKeys_Sky, gradientAlphaKeys);
GradientColorKey[] gradientColorKeys_Equator = new GradientColorKey[] {
new GradientColorKey(color_Equator_Night, time_Sunrise1),
new GradientColorKey(color_Equator_Twilight, time_Sunrise2),
new GradientColorKey(color_Equator_Day, time_Early),
new GradientColorKey(color_Equator_Day, time_Late),
new GradientColorKey(color_Equator_Twilight, time_Sunset1),
new GradientColorKey(color_Equator_Night, time_Sunset2), };
gradient_Equator.SetKeys(gradientColorKeys_Equator, gradientAlphaKeys);
GradientColorKey[] gradientColorKeys_Ground = new GradientColorKey[] {
new GradientColorKey(color_Ground_Night, time_Sunrise1),
new GradientColorKey(color_Ground_Twilight, time_Sunrise2),
new GradientColorKey(color_Ground_Day, time_Early),
new GradientColorKey(color_Ground_Day, time_Late),
new GradientColorKey(color_Ground_Twilight, time_Sunset1),
new GradientColorKey(color_Ground_Night, time_Sunset2), };
gradient_Ground.SetKeys(gradientColorKeys_Ground, gradientAlphaKeys);
// Assigning the new color gradients.
UnityEngine.Object.FindObjectOfType<DayNightProperties>().m_LightColor = gradient_Direct;
var ambient = typeof(DayNightProperties.AmbientColor);
ambient.GetField("m_SkyColor", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(DayNightProperties.instance.m_AmbientColor, gradient_Sky);
ambient.GetField("m_EquatorColor", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(DayNightProperties.instance.m_AmbientColor, gradient_Equator);
ambient.GetField("m_GroundColor", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(DayNightProperties.instance.m_AmbientColor, gradient_Ground);
}
public static void CalculateTonemapping(float brightness, float gamma, float contrast)
{
var calc_Contrast = contrast * 1.4f;
var toneMap = GameObject.Find("Main Camera").GetComponent<ColossalFramework.ToneMapping>();
toneMap.m_Luminance = 0.10f + (calc_Contrast * 0.013f);
toneMap.m_ToneMappingBoostFactor = (1.00f + (0.5f * brightness)) / (float)Math.Pow(tweak_Intensity, 2.2f);
toneMap.m_ToneMappingGamma = 2.60f * (((gamma + 1) / 4) + 0.75f);
toneMap.m_ToneMappingParamsFilmic.A = 0.50f + (calc_Contrast * 0.13f);
toneMap.m_ToneMappingParamsFilmic.B = 0.25f - (calc_Contrast * 0.08f);
toneMap.m_ToneMappingParamsFilmic.C = 0.10f - (calc_Contrast * 0.005f);
toneMap.m_ToneMappingParamsFilmic.D = 0.70f + (calc_Contrast * 0.13f);
toneMap.m_ToneMappingParamsFilmic.E = 0.01f;
toneMap.m_ToneMappingParamsFilmic.F = 0.25f - (calc_Contrast * 0.11f);
toneMap.m_ToneMappingParamsFilmic.W = 11.20f + calc_Contrast;
}
public static void SkyTonemapping(bool enable)
{
var daynight = UnityEngine.Object.FindObjectOfType<DayNightProperties>();
daynight.m_Tonemapping = enable;
}
#endregion
}
[HarmonyPatch(typeof(DayNightProperties))]
[HarmonyPatch("UpdateLighting")]
class UpdateLighting
{
static void Postfix()
{
RenderManager.instance.MainLight.shadowBias = LightingRebalanceMod.GetShadowBias();
}
}
}
public class LightPreset
{
public LightPreset() { }
public float[] m_values;
public bool m_skyTonemapping, m_canBeDeleted;
public string m_presetName, m_filePath;
public void SavePreset(bool addToPresetsList)
{
string path = DataLocation.localApplicationData + @"\ModConfig\RelightPresets\";
if (!Directory.Exists(DataLocation.localApplicationData + @"\ModConfig\"))
Directory.CreateDirectory(DataLocation.localApplicationData + @"\ModConfig\");
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
this.m_canBeDeleted = true;
this.m_filePath = path + ToFileName(this.m_presetName) + ".light";
TextWriter tw = new StreamWriter(this.m_filePath);
tw.WriteLine("name = " + ((this.m_presetName == "") ? "[none]" : this.m_presetName));
tw.WriteLine("0 = " + this.m_values[0]);
tw.WriteLine("1 = " + this.m_values[1]);
tw.WriteLine("2 = " + this.m_values[2]);
tw.WriteLine("3 = " + this.m_values[3]);
tw.WriteLine("4 = " + this.m_values[4]);
tw.WriteLine("5 = " + this.m_values[5]);
tw.WriteLine("6 = " + this.m_values[6]);
tw.WriteLine("7 = " + this.m_values[7]);
tw.WriteLine("8 = " + this.m_values[8]);
tw.WriteLine("9 = " + this.m_values[9]);
tw.WriteLine("10 = " + this.m_values[10]);
tw.WriteLine("11 = " + this.m_values[11]);
tw.WriteLine("12 = " + this.m_values[12]);
tw.WriteLine("skyTmpg = " + this.m_skyTonemapping);
tw.Close();
if (addToPresetsList)
LightPresets.Add(this);
}
public static void LoadLightPresets()
{
LightPresets = new List<LightPreset>();
string path = DataLocation.localApplicationData + @"\ModConfig\RelightPresets\";
if (!Directory.Exists(DataLocation.localApplicationData + @"\ModConfig\"))
Directory.CreateDirectory(DataLocation.localApplicationData + @"\ModConfig\");
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
foreach (string file in Directory.GetFiles(path, "*.light", SearchOption.AllDirectories))
{
var preset = new LightPreset();
preset.m_presetName = "[none]";
preset.m_canBeDeleted = true;
preset.m_filePath = file;
var dict = new Dictionary<int, float>();
foreach (string line in File.ReadAllLines(file).Where(s => s.Contains(" = ")))
{
string[] data = line.Split(new string[] { " = " }, StringSplitOptions.RemoveEmptyEntries);
if (data[0] == "skyTmpg")
{
preset.m_skyTonemapping = bool.Parse(data[1]);
}
else if (data[0] == "name")
{
preset.m_presetName = data[1];
}
else
{
var id = int.Parse(data[0]);
dict[id] = 0;
dict[id] = float.Parse(data[1]);
}
}
preset.m_values = new float[]
{
dict[0], dict[1], dict[2], dict[3], dict[4], dict[5], dict[6], dict[7], dict[8], dict[9], dict[10], dict[11], dict[12]
};
LightPresets.Add(preset);
}
foreach (PublishedFileId fileId in PlatformService.workshop.GetSubscribedItems())
{
string fileDir = PlatformService.workshop.GetSubscribedItemPath(fileId);
if (fileDir == null)
continue;
if (fileDir == "")
continue;
if (!Directory.Exists(fileDir))
continue;
var files = Directory.GetFiles(fileDir, "*.light", SearchOption.AllDirectories);
if (files.Any())
{
foreach (string file in files)
{
var preset = new LightPreset();
preset.m_presetName = "[none]";
preset.m_canBeDeleted = false;
var dict = new Dictionary<int, float>();
foreach (string line in File.ReadAllLines(file).Where(s => s.Contains(" = ")))
{
string[] data = line.Split(new string[] { " = " }, StringSplitOptions.RemoveEmptyEntries);
if (data[0] == "skyTmpg")
{
preset.m_skyTonemapping = bool.Parse(data[1]);
}
else if (data[0] == "name")
{
preset.m_presetName = data[1];
}
else
{
var id = int.Parse(data[0]);
dict[id] = 0;
dict[id] = float.Parse(data[1]);
}
}
preset.m_values = new float[]
{
dict[0], dict[1], dict[2], dict[3], dict[4], dict[5], dict[6], dict[7], dict[8], dict[9], dict[10], dict[11], dict[12]
};
LightPresets.Add(preset);
}
}
}
}
public static void DeletePreset(LightPreset preset)
{
if (preset.m_canBeDeleted)
{
if (File.Exists(preset.m_filePath))
File.Delete(preset.m_filePath);
if (LightPresets.Contains(preset))
LightPresets.Remove(preset);
}
}
public static List<LightPreset> LightPresets;
public static string ToFileName(string s)
{
return s.Replace(" ", "_").Replace(@"\", "").Replace("/", "").Replace("|", "").Replace("<", "").Replace(">", "").Replace("*", "").Replace(":", "").Replace("?", "").Replace("\"", "");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment