Created
July 16, 2018 15:57
-
-
Save ronyx69/68d57f77e721dabefaefa864cc29616b to your computer and use it in GitHub Desktop.
Source code for the Detail mod.
This file contains hidden or 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 ColossalFramework.IO; | |
using ColossalFramework.UI; | |
using ICities; | |
using System; | |
using System.IO; | |
using System.Reflection; | |
using System.Xml.Serialization; | |
using UnityEngine; | |
namespace Detail | |
{ | |
public class HotkeyBehaviour : MonoBehaviour | |
{ | |
void Update() | |
{ | |
if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.LeftAlt) && Input.GetKeyDown(KeyCode.D)) | |
{ | |
var settings = DetailMod.Settings; | |
if (settings.distance == 0f) settings.distance = 1f; | |
else settings.distance = 0f; | |
DetailMod.UpdateDistanceSliderFromSettings(); | |
Detail.SetRenderPropertiesAll(); | |
} | |
} | |
} | |
public class DetailMod : IUserMod | |
{ | |
public string Name => "Detail"; | |
public string Description => "Allows colorable vegetation and custom render distance for props."; | |
private static Configuration settings; | |
public static Configuration Settings | |
{ | |
get | |
{ | |
if (settings == null) | |
{ | |
settings = Configuration.LoadConfiguration(); | |
if (settings == null) | |
{ | |
settings = new Configuration(); | |
Configuration.SaveConfiguration(); | |
} | |
} | |
return settings; | |
} | |
set | |
{ | |
settings = value; | |
} | |
} | |
#region UserInterface | |
private float sliderWidth = 715f; | |
private float sliderHeight = 16f; | |
private string toolTipText = "Hold SHIFT to drag all sliders"; | |
private UISlider redSlider; | |
private UISlider greenSlider; | |
private UISlider blueSlider; | |
private UISlider brightnessSlider; | |
private UISlider colorSlider; | |
private static UISlider distanceSlider; | |
private UIButton matchButton; | |
private static void UpdateSlider(UISlider slider, float value) | |
{ | |
slider.value = value; | |
var rootPanel = slider.parent as UIPanel; | |
var label = rootPanel.Find<UILabel>("Label"); | |
label.text = value.ToString(); | |
} | |
public static void UpdateDistanceSliderFromSettings() | |
{ | |
distanceSlider.value = settings.distance; | |
} | |
public static void UpdateSlider2(UISlider slider, float value) | |
{ | |
slider.value = value; | |
} | |
public void OnSettingsUI(UIHelperBase helper) | |
{ | |
UIHelperBase mainGroup = helper.AddGroup(" Detail"); | |
distanceSlider = (UISlider)mainGroup.AddSlider("Render Distance", 0f, 1f, 0.1f, Settings.distance, (f) => | |
{ | |
distanceSlider.tooltipBox.isVisible = true; | |
Settings.distance = f; | |
UpdateSlider2(distanceSlider, f); | |
Detail.SetRenderPropertiesAll(); | |
}); | |
distanceSlider.color = Color.gray; | |
distanceSlider.scrollWheelAmount = 0.1f; | |
distanceSlider.width = sliderWidth; | |
distanceSlider.height = sliderHeight; | |
UpdateSlider2(distanceSlider, Settings.distance); | |
mainGroup.AddSpace(32); | |
UIHelperBase sliderGroup = helper.AddGroup(" Vegetation Color"); | |
redSlider = (UISlider)sliderGroup.AddSlider(Settings.VegetationColor.r.ToString(), 0f, 255f, 1f, Settings.VegetationColor.r, (f) => | |
{ | |
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) | |
{ | |
var difference = f - Settings.VegetationColor.r; | |
var green = Settings.VegetationColor.g; | |
var blue = Settings.VegetationColor.b; | |
if (blue + difference >= 0 && blue + difference <= 255) | |
{ | |
UpdateSlider(blueSlider, blue + difference); | |
Settings.VegetationColor.b = blue + difference; | |
} | |
if (green + difference >= 0 && green + difference <= 255) | |
{ | |
UpdateSlider(greenSlider, green + difference); | |
Settings.VegetationColor.g = green + difference; | |
} | |
redSlider.tooltipBox.isVisible = false; | |
} | |
else redSlider.tooltipBox.isVisible = true; | |
Settings.VegetationColor.r = f; | |
UpdateSlider(redSlider, f); | |
Detail.ApplyColors(); | |
}); | |
redSlider.color = Color.Lerp(Color.red, Color.white, 0.25f); | |
redSlider.tooltip = toolTipText; | |
redSlider.scrollWheelAmount = 1f; | |
redSlider.width = sliderWidth; | |
redSlider.height = sliderHeight; | |
UpdateSlider(redSlider, Settings.VegetationColor.r); | |
greenSlider = (UISlider)sliderGroup.AddSlider(Settings.VegetationColor.g.ToString(), 0f, 255f, 1f, Settings.VegetationColor.g, (f) => | |
{ | |
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) | |
{ | |
var difference = f - Settings.VegetationColor.g; | |
var red = Settings.VegetationColor.r; | |
var blue = Settings.VegetationColor.b; | |
if (red + difference >= 0 && red + difference <= 255) | |
{ | |
UpdateSlider(redSlider, red + difference); | |
Settings.VegetationColor.r = red + difference; | |
} | |
if (blue + difference >= 0 && blue + difference <= 255) | |
{ | |
UpdateSlider(blueSlider, blue + difference); | |
Settings.VegetationColor.b = blue + difference; | |
} | |
greenSlider.tooltipBox.isVisible = false; | |
} | |
else greenSlider.tooltipBox.isVisible = true; | |
greenSlider.RefreshTooltip(); | |
Settings.VegetationColor.g = f; | |
UpdateSlider(greenSlider, f); | |
Detail.ApplyColors(); | |
}); | |
greenSlider.color = Color.Lerp(Color.green, Color.white, 0.25f); | |
greenSlider.tooltip = toolTipText; | |
greenSlider.scrollWheelAmount = 1f; | |
greenSlider.width = sliderWidth; | |
greenSlider.height = sliderHeight; | |
UpdateSlider(greenSlider, Settings.VegetationColor.g); | |
blueSlider = (UISlider)sliderGroup.AddSlider(Settings.VegetationColor.b.ToString(), 0f, 255f, 1f, Settings.VegetationColor.b, (f) => | |
{ | |
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) | |
{ | |
var difference = f - Settings.VegetationColor.b; | |
var red = Settings.VegetationColor.r; | |
var green = Settings.VegetationColor.g; | |
if (red + difference >= 0 && red + difference <= 255) | |
{ | |
UpdateSlider(redSlider, red + difference); | |
Settings.VegetationColor.r = red + difference; | |
} | |
if (green + difference >= 0 && green + difference <= 255) | |
{ | |
UpdateSlider(greenSlider, green + difference); | |
Settings.VegetationColor.g = green + difference; | |
} | |
blueSlider.tooltipBox.isVisible = false; | |
} | |
else blueSlider.tooltipBox.isVisible = true; | |
blueSlider.RefreshTooltip(); | |
Settings.VegetationColor.b = f; | |
UpdateSlider(blueSlider, f); | |
Detail.ApplyColors(); | |
}); | |
blueSlider.color = Color.Lerp(Color.blue, Color.white, 0.25f); | |
blueSlider.tooltip = toolTipText; | |
blueSlider.scrollWheelAmount = 1f; | |
blueSlider.width = sliderWidth; | |
blueSlider.height = sliderHeight; | |
UpdateSlider(blueSlider, Settings.VegetationColor.b); | |
sliderGroup.AddSpace(24); | |
matchButton = (UIButton)sliderGroup.AddButton("Auto Match Grass", () => | |
{ | |
Detail.MatchColor(); | |
UpdateSlider(redSlider, Settings.VegetationColor.r); | |
UpdateSlider(greenSlider, Settings.VegetationColor.g); | |
UpdateSlider(blueSlider, Settings.VegetationColor.b); | |
}); | |
sliderGroup.AddSpace(32); | |
brightnessSlider = (UISlider)sliderGroup.AddSlider("Brightness Randomness", 0f, 1f, 0.1f, Settings.randomBrightness, (f) => | |
{ | |
brightnessSlider.tooltipBox.isVisible = true; | |
Settings.randomBrightness = f; | |
UpdateSlider2(brightnessSlider, f); | |
Detail.ApplyColors(); | |
}); | |
brightnessSlider.color = Color.gray; | |
brightnessSlider.scrollWheelAmount = 0.1f; | |
brightnessSlider.width = sliderWidth; | |
brightnessSlider.height = sliderHeight; | |
UpdateSlider2(brightnessSlider, Settings.randomBrightness); | |
sliderGroup.AddSpace(8); | |
colorSlider = (UISlider)sliderGroup.AddSlider("Color Randomness", 0f, 1f, 0.1f, Settings.randomColor, (f) => | |
{ | |
colorSlider.tooltipBox.isVisible = true; | |
Settings.randomColor = f; | |
UpdateSlider2(colorSlider, f); | |
Detail.ApplyColors(); | |
}); | |
colorSlider.color = Color.gray; | |
colorSlider.scrollWheelAmount = 0.1f; | |
colorSlider.width = sliderWidth; | |
colorSlider.height = sliderHeight; | |
UpdateSlider2(colorSlider, Settings.randomColor); | |
//add hotkey behaviour | |
var uiHelper = helper as UIHelper; | |
var uiComponent = uiHelper.self as UIComponent; | |
uiComponent.gameObject.AddComponent<HotkeyBehaviour>(); | |
} | |
#endregion | |
} | |
public class Configuration | |
{ | |
[XmlIgnore] | |
private static readonly string configurationPath = Path.Combine(DataLocation.localApplicationData, "Detail.xml"); | |
public Color VegetationColor = new Color(255f, 255f, 255f, 1f); | |
public float randomBrightness = 0.5f; | |
public float randomColor = 0.5f; | |
public float distance = 1f; | |
public bool AutoDeployed = false; | |
public Configuration() { } | |
public void OnPreSerialize() { } | |
public void OnPostDeserialize() { } | |
public static void SaveConfiguration() | |
{ | |
var fileName = configurationPath; | |
var config = DetailMod.Settings; | |
var serializer = new XmlSerializer(typeof(Configuration)); | |
using (var writer = new StreamWriter(fileName)) | |
{ | |
config.OnPreSerialize(); | |
serializer.Serialize(writer, config); | |
} | |
} | |
public static Configuration LoadConfiguration() | |
{ | |
var fileName = configurationPath; | |
var serializer = new XmlSerializer(typeof(Configuration)); | |
try | |
{ | |
using (var reader = new StreamReader(fileName)) | |
{ | |
var config = serializer.Deserialize(reader) as Configuration; | |
return config; | |
} | |
} | |
catch (Exception ex) | |
{ | |
Debug.Log(string.Format("[Detail]: Error Parsing {0}: {1}", fileName, ex.Message.ToString())); | |
return null; | |
} | |
} | |
} | |
public class DetailLoading : LoadingExtensionBase | |
{ | |
public override void OnLevelLoaded(LoadMode mode) | |
{ | |
base.OnLevelLoaded(mode); | |
UIView.library.Get<UIPanel>("OptionsPanel").eventVisibilityChanged += (c, i) => Detail.SetRenderPropertiesAll(); | |
UIView.GetAView().FindUIComponent<UIDropDown>("LevelOfDetail").eventSelectedIndexChanged += (c, i) => Detail.SetRenderPropertiesAll(); | |
} | |
public override void OnLevelUnloading() | |
{ | |
base.OnLevelUnloading(); | |
Detail.Loaded = false; | |
UIView.library.Get<UIPanel>("OptionsPanel").eventVisibilityChanged -= (c, i) => Detail.SetRenderPropertiesAll(); | |
UIView.GetAView().FindUIComponent<UIDropDown>("LevelOfDetail").eventSelectedIndexChanged -= (c, i) => Detail.SetRenderPropertiesAll(); | |
} | |
} | |
public class Detail | |
{ | |
public static bool Loaded; | |
internal static void SetRenderProperties(PropInfo prefab) | |
{ | |
string[] values = prefab.m_material.name.Split(' '); | |
prefab.m_lodRenderDistance = Convert.ToSingle(values[1]) * DetailMod.Settings.distance; | |
prefab.m_maxRenderDistance = Convert.ToSingle(values[2]) * DetailMod.Settings.distance; | |
} | |
internal static void SetRenderProperties(TreeInfo prefab) | |
{ | |
prefab.m_renderMaterial.SetTexture("_MainTex", prefab.m_material.GetTexture("_MainTex")); | |
prefab.m_renderMaterial.SetTexture("_XYCAMap", prefab.m_material.GetTexture("_XYCAMap")); | |
} | |
internal static void SetRenderPropertiesAll() | |
{ | |
for (uint i = 0; i < PrefabCollection<PropInfo>.LoadedCount(); i++) | |
{ | |
var prefab = PrefabCollection<PropInfo>.GetLoaded(i); | |
if (prefab == null) continue; | |
if (prefab.m_material.name.Contains("DetailMod_")) SetRenderProperties(prefab); | |
} | |
for (uint i = 0; i < PrefabCollection<TreeInfo>.LoadedCount(); i++) | |
{ | |
var prefab = PrefabCollection<TreeInfo>.GetLoaded(i); | |
if (prefab == null) continue; | |
if (prefab.m_material.name.Contains("DetailMod_Tree")) SetRenderProperties(prefab); | |
} | |
} | |
internal static void ApplyProperties() | |
{ | |
SetRenderPropertiesAll(); | |
ApplyColors(); | |
} | |
internal static void MatchColor() | |
{ | |
var correction = 0.53f; var emphasis = 6; | |
var tex = UnityEngine.Object.FindObjectOfType<TerrainProperties>().m_grassDiffuse; | |
var texColors = tex.GetPixels32(); var total = texColors.Length; | |
var r = 0; var g = 0; var b = 0; | |
for (var i = 0; i < total; i++) | |
{ | |
r += texColors[i].r; g += texColors[i].g; b += texColors[i].b; | |
} | |
r = r / total; g = g / total; b = b / total; | |
if (r > g && r > b) | |
{ | |
r = r + emphasis; | |
if (g > b) g = g + Mathf.RoundToInt(emphasis / 2f); | |
else if (b > g) b = b + Mathf.RoundToInt(emphasis / 2f); | |
} | |
else if (g > r && g > b) | |
{ | |
g = g + emphasis; | |
if (r > b) r = r + Mathf.RoundToInt(emphasis / 2f); | |
else if (b > r) b = b + Mathf.RoundToInt(emphasis / 2f); | |
} | |
else if (b > r && b > g) | |
{ | |
b = b + emphasis; | |
if (r > g) r = r + Mathf.RoundToInt(emphasis / 2f); | |
else if (g > r) g = g + Mathf.RoundToInt(emphasis / 2f); | |
} | |
DetailMod.Settings.VegetationColor.r = Mathf.RoundToInt(Mathf.Pow((r / 255f), correction) * 255); | |
DetailMod.Settings.VegetationColor.g = Mathf.RoundToInt(Mathf.Pow((g / 255f), correction) * 255); | |
DetailMod.Settings.VegetationColor.b = Mathf.RoundToInt(Mathf.Pow((b / 255f), correction) * 255); | |
ApplyColors(); | |
} | |
internal static void ApplyColors() | |
{ | |
var rgb = DetailMod.Settings.VegetationColor; | |
var randomBrightness = DetailMod.Settings.randomBrightness; | |
var randomColor = DetailMod.Settings.randomColor; | |
Color colord = new Color( | |
Mathf.Pow((rgb.r / 255f), (1.0f)), | |
Mathf.Pow((rgb.g / 255f), (1.0f)), | |
Mathf.Pow((rgb.b / 255f), (1.0f))); | |
Color color0 = new Color( | |
Mathf.Pow((rgb.r / 255f), (1.0f + (randomBrightness * 0.2f))), | |
Mathf.Pow((rgb.g / 255f), (1.0f + (randomBrightness * 0.2f) - (randomColor * 0.1f))), | |
Mathf.Pow((rgb.b / 255f), (1.0f + (randomBrightness * 0.2f)))); | |
Color color1 = new Color( | |
Mathf.Pow((rgb.r / 255f), (1.0f + (randomBrightness * 0.1f) - (randomColor * 0.05f))), | |
Mathf.Pow((rgb.g / 255f), (1.0f + (randomBrightness * 0.1f))), | |
Mathf.Pow((rgb.b / 255f), (1.0f + (randomBrightness * 0.1f) + (randomColor * 0.05f)))); | |
Color color2 = new Color( | |
Mathf.Pow((rgb.r / 255f), (1.0f - (randomBrightness * 0.1f) + (randomColor * 0.05f))), | |
Mathf.Pow((rgb.g / 255f), (1.0f - (randomBrightness * 0.1f))), | |
Mathf.Pow((rgb.b / 255f), (1.0f - (randomBrightness * 0.1f) - (randomColor * 0.05f)))); | |
Color color3 = new Color( | |
Mathf.Pow((rgb.r / 255f), (1.0f - (randomBrightness * 0.2f))), | |
Mathf.Pow((rgb.g / 255f), (1.0f - (randomBrightness * 0.2f) + (randomColor * 0.1f))), | |
Mathf.Pow((rgb.b / 255f), (1.0f - (randomBrightness * 0.2f)))); | |
for (uint i = 0; i < PrefabCollection<PropInfo>.LoadedCount(); i++) | |
{ | |
var prefab = PrefabCollection<PropInfo>.GetLoaded(i); | |
if (prefab == null) continue; | |
if (prefab.m_material.name.Contains("DetailMod_Vegetation")) | |
{ | |
prefab.m_color0 = color0; prefab.m_color1 = color1; | |
prefab.m_color2 = color2; prefab.m_color3 = color3; | |
} | |
} | |
for (uint i = 0; i < PrefabCollection<TreeInfo>.LoadedCount(); i++) | |
{ | |
var prefab = PrefabCollection<TreeInfo>.GetLoaded(i); | |
if (prefab == null) continue; | |
if (prefab.m_material.name.Contains("DetailMod_Tree")) | |
{ | |
prefab.m_defaultColor = colord; | |
prefab.m_material.color = colord; | |
prefab.m_renderMaterial.color = colord; | |
prefab.m_maxBrightness = 1.0f + (randomBrightness * 0.2f); | |
prefab.m_minBrightness = 1.0f - (randomBrightness * 0.2f); | |
} | |
} | |
Configuration.SaveConfiguration(); | |
SetRenderPropertiesAll(); | |
} | |
} | |
public class DisableBlur : ThreadingExtensionBase | |
{ | |
private UIComponent component; | |
public override void OnUpdate(float realTimeDelta, float simulationTimeDelta) | |
{ | |
if (!Detail.Loaded && LoadingManager.instance.m_loadingComplete) | |
{ | |
Detail.ApplyProperties(); | |
Detail.Loaded = true; | |
} | |
if (component == null) component = UIView.library.Get("OptionsPanel"); | |
if (component != null && component.isVisible) | |
{ | |
UITextureSprite uITextureSprite = Util.ReadPrivate<DesaturationControl, UITextureSprite>(UnityEngine.Object.FindObjectOfType<DesaturationControl>(), "m_Target"); | |
if (uITextureSprite.opacity != 0f) | |
{ | |
uITextureSprite.opacity = 0f; | |
Util.WritePrivate<DesaturationControl, UITextureSprite>(UnityEngine.Object.FindObjectOfType<DesaturationControl>(), "m_Target", uITextureSprite); | |
} | |
} | |
} | |
} | |
public static class Util | |
{ | |
public static Q ReadPrivate<T, Q>(T o, string fieldName) | |
{ | |
FieldInfo[] fields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | |
FieldInfo fieldInfo = null; | |
FieldInfo[] array = fields; | |
for (int i = 0; i < array.Length; i++) | |
{ | |
FieldInfo fieldInfo2 = array[i]; | |
if (fieldInfo2.Name == fieldName) | |
{ | |
fieldInfo = fieldInfo2; | |
break; | |
} | |
} | |
return (Q)((object)fieldInfo.GetValue(o)); | |
} | |
public static void WritePrivate<T, Q>(T o, string fieldName, object value) | |
{ | |
FieldInfo[] fields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | |
FieldInfo fieldInfo = null; | |
FieldInfo[] array = fields; | |
for (int i = 0; i < array.Length; i++) | |
{ | |
FieldInfo fieldInfo2 = array[i]; | |
if (fieldInfo2.Name == fieldName) | |
{ | |
fieldInfo = fieldInfo2; | |
break; | |
} | |
} | |
fieldInfo.SetValue(o, value); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment