Skip to content

Instantly share code, notes, and snippets.

@ronyx69
Last active August 31, 2022 15:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ronyx69/280e5695b3caeac7ef8b356cc36e0a7c to your computer and use it in GitHub Desktop.
Save ronyx69/280e5695b3caeac7ef8b356cc36e0a7c to your computer and use it in GitHub Desktop.
Source code for Realtime mod. Ingame texture and mesh replacement. UI by Simon Royer
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.IO;
using ColossalFramework.IO;
using ObjUnity3D;
using ICities;
namespace Realtime
{
public class Realtime_Mod : LoadingExtensionBase, IUserMod
{
public static GameObject gameObject;
public override void OnLevelLoaded(LoadMode mode)
{
base.OnLevelLoaded(mode);
if (mode != LoadMode.NewGame && mode != LoadMode.LoadGame)
{
return;
}
if (gameObject != null)
{
return;
}
gameObject = new GameObject("Realtime_Mod");
gameObject.AddComponent<RealtimeUI>();
}
public override void OnLevelUnloading()
{
base.OnLevelUnloading();
if (gameObject == null)
{
return;
}
UnityEngine.Object.Destroy(gameObject);
gameObject = null;
}
public string Name
{
get { return "Realtime"; }
}
public string Description
{
get { return "Realtime"; }
}
}
public class RealtimeUI : MonoBehaviour
{
// Global variables
public bool showWindow;
private Rect windowRect;
private bool uiMode = true; // true = main | false = asset specified
// Search variables
private string searchToolbar = string.Empty;
private List<PropInfo> searchedProps;
private List<VehicleInfo> searchedVehicles;
private List<TreeInfo> searchedTrees;
private List<BuildingInfo> searchedBuildings;
private Vector2 searchScrollPos = Vector2.zero;
// Selected Asset variables
public string assetType;
public PropInfo selectedProp;
public VehicleInfo selectedVehicle;
public TreeInfo selectedTree;
public BuildingInfo selectedBuilding;
// Replacement UI variables
private string FilesLocation;
private string filesPrefix = string.Empty;
private string[] texSelectionStrings = new string[] { "Normal", "LOD" };
// Sub Buildings variables
private bool scrollingThroughSubBuildings = false;
private Vector2 scrollSubPosition = Vector2.zero;
// MonoBehaviour functions
void Start()
{
FilesLocation = DataLocation.addonsPath + "\\Import\\";
windowRect = new Rect(150, 150, 500, 600);
searchedProps = new List<PropInfo>();
searchedVehicles = new List<VehicleInfo>();
searchedTrees = new List<TreeInfo>();
searchedBuildings = new List<BuildingInfo>();
}
void OnGUI()
{
if (showWindow)
windowRect = GUI.Window(this.GetInstanceID(), windowRect, DrawWindow, "Realtime Replacement");
}
void Update()
{
if (Input.GetKey(KeyCode.LeftShift))
{
if (Input.GetKey(KeyCode.LeftAlt))
{
if (Input.GetKeyDown(KeyCode.R))
{
showWindow = !showWindow;
}
}
}
}
// Window UI function
void DrawWindow(int winID)
{
GUI.DragWindow(new Rect(0, 0, 460, 30));
if (RealtimeUtils.IsDroppableToolEnabled() && windowRect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)))
{
if (GUI.Button(new Rect(10, 40, 480, 550), "<size=42>▼</size>\n<size=70>Drop</size>\n<size=42>asset\nhere</size>"))
{
if (TryDropPrefab())
{
RealtimeUtils.ResetTool();
uiMode = false;
}
}
}
else
{
if (uiMode)
{
if (GUI.Button(new Rect(465, 3, 30, 28), "X"))
showWindow = false;
GUI.Label(new Rect(10, 40, 480, 30), "Search for an asset to edit, or drop it on the window.");
searchToolbar = GUI.TextField(new Rect(10, 68, 400, 28), searchToolbar);
if (GUI.Button(new Rect(415, 68, 75, 28), "Search"))
SearchPrefabs(searchToolbar);
GUI.Box(new Rect(10, 100, 480, 490), string.Empty);
if ((searchedProps.Count + searchedVehicles.Count + searchedTrees.Count + searchedBuildings.Count) == 0)
GUI.Label(new Rect(20, 110, 460, 30), "No asset match this string !");
else
{
searchScrollPos = GUI.BeginScrollView(new Rect(15, 105, 470, 480), searchScrollPos, new Rect(0, 0, 450, 43 * searchedProps.Count + 43 * searchedVehicles.Count + 43 * searchedBuildings.Count + 43 * searchedTrees.Count));
int yPos = 0;
for (int i = 0; i < searchedProps.Count; i++)
{
GUI.Box(new Rect(2, yPos + 2, 448, 40), string.Empty);
GUI.color = Color.cyan;
GUI.Label(new Rect(10, yPos + 8, 55, 30), "[Prop]");
GUI.color = Color.white;
GUI.Label(new Rect(65, yPos + 5, 280, 38), searchedProps[i].GetLocalizedTitle().Replace("PROPS_TITLE[", "").Replace("]:0", "") + "\n(<i>" + searchedProps[i].name + "</i>)");
if (GUI.Button(new Rect(350, yPos + 9, 100, 27), "More ►"))
SelectPrefab<PropInfo>(searchedProps[i]);
yPos += 43;
}
for (int i = 0; i < searchedBuildings.Count; i++)
{
GUI.Box(new Rect(2, yPos + 2, 448, 40), string.Empty);
GUI.color = Color.red;
GUI.Label(new Rect(10, yPos + 8, 55, 30), "[Building]");
GUI.color = Color.white;
GUI.Label(new Rect(65, yPos + 5, 280, 38), searchedBuildings[i].GetLocalizedTitle().Replace("BUILDING_TITLE[", "").Replace("]:0", "") + "\n(<i>" + searchedBuildings[i].name + "</i>)");
if (GUI.Button(new Rect(350, yPos + 9, 100, 27), "More ►"))
SelectPrefab<BuildingInfo>(searchedBuildings[i]);
yPos += 43;
}
for (int i = 0; i < searchedVehicles.Count; i++)
{
GUI.Box(new Rect(2, yPos + 2, 448, 40), string.Empty);
GUI.color = Color.yellow;
GUI.Label(new Rect(10, yPos + 8, 55, 30), "[Vehicle]");
GUI.color = Color.white;
GUI.Label(new Rect(65, yPos + 5, 280, 38), searchedVehicles[i].GetLocalizedTitle() + "\n(<i>" + searchedVehicles[i].name + "</i>)");
if (GUI.Button(new Rect(350, yPos + 9, 100, 27), "More ►"))
SelectPrefab<VehicleInfo>(searchedVehicles[i]);
yPos += 43;
}
for (int i = 0; i < searchedTrees.Count; i++)
{
GUI.Box(new Rect(2, yPos + 2, 448, 40), string.Empty);
GUI.color = Color.green;
GUI.Label(new Rect(10, yPos + 8, 55, 30), "[Tree]");
GUI.color = Color.white;
GUI.Label(new Rect(65, yPos + 5, 280, 38), searchedTrees[i].GetLocalizedTitle() + "\n(<i>" + searchedTrees[i].name + "</i>)");
if (GUI.Button(new Rect(350, yPos + 9, 100, 27), "More ►"))
SelectPrefab<TreeInfo>(searchedTrees[i]);
yPos += 43;
}
GUI.EndScrollView();
}
}
else // => if uiMode == false
{
if (scrollingThroughSubBuildings)
{
if (GUI.Button(new Rect(465, 3, 30, 28), "←"))
{
scrollingThroughSubBuildings = false;
scrollSubPosition = Vector2.zero;
}
GUI.Label(new Rect(10, 35, 480, 35), "<size=22>" + selectedBuilding.GetLocalizedTitle().RemoveMissingTranslationString().PossessionApostrophe() + " Sub-buildings" + "</size>");
GUI.Label(new Rect(10, 68, 410, 28), "Choose a sub building, or click 'Back'");
if (GUI.Button(new Rect(430, 68, 60, 28), "Back"))
{
scrollingThroughSubBuildings = false;
scrollSubPosition = Vector2.zero;
}
GUI.Box(new Rect(10, 100, 480, 490), string.Empty);
scrollSubPosition = GUI.BeginScrollView(new Rect(15, 105, 470, 480), scrollSubPosition, new Rect(0, 0, 450, selectedBuilding.m_subBuildings.Count() * 43));
int yPos = 0;
for (int i = 0; i < selectedBuilding.m_subBuildings.Count(); i++)
{
GUI.Box(new Rect(2, yPos + 2, 448, 40), string.Empty);
GUI.color = Color.red;
GUI.Label(new Rect(10, yPos + 8, 55, 30), "[Building]");
GUI.color = Color.white;
GUI.Label(new Rect(65, yPos + 5, 280, 38), selectedBuilding.m_subBuildings[i].m_buildingInfo.GetLocalizedTitle().Replace("BUILDING_TITLE[", "").Replace("]:0", "") + "\n(<i>" + selectedBuilding.m_subBuildings[i].m_buildingInfo.name + "</i>)");
if (GUI.Button(new Rect(350, yPos + 9, 100, 27), "More ►"))
{
scrollingThroughSubBuildings = false;
scrollSubPosition = Vector2.zero;
SelectPrefab<BuildingInfo>(selectedBuilding.m_subBuildings[i].m_buildingInfo);
}
yPos += 43;
}
GUI.EndScrollView();
}
else
{
if (GUI.Button(new Rect(465, 3, 30, 28), "←"))
ResetSelection();
switch (assetType)
{
case "prop":
GUI.color = Color.cyan;
GUI.Label(new Rect(10, 40, 55, 30), "[Prop]");
break;
case "building":
GUI.color = Color.red;
GUI.Label(new Rect(10, 40, 55, 30), "[Building]");
break;
case "tree":
GUI.color = Color.green;
GUI.Label(new Rect(10, 40, 55, 30), "[Tree]");
break;
case "vehicle":
GUI.color = Color.yellow;
GUI.Label(new Rect(10, 38, 55, 30), "[Vehicle]");
break;
}
GUI.color = Color.white;
GUI.Label(new Rect(10, 60, 480, 35), "<size=25>" + this.SelectedAsPrefabInfo().GetLocalizedTitle().RemoveMissingTranslationString() + "</size>");
GUI.Label(new Rect(10, 88, 480, 30), "Asset name : <i>" + this.SelectedAsPrefabInfo().name + "</i>");
// Mesh replacement UI
GUI.Box(new Rect(10, 118, 480, 35), string.Empty);
// Mesh Text field
GUI.Label(new Rect(16, 126, 130, 25), "Files Prefix");
filesPrefix = GUI.TextField(new Rect(130, 122, 350, 26), filesPrefix);
GUI.Box(new Rect(10, 160, 480, 290), string.Empty);
// Mesh Replacement
GUI.Label(new Rect(190, 165, 480, 28), "<b>Meshes Replacement</b>");
RealtimeUtils.UIReplace<string, string>(new Vector2(20, 195), "Mesh file", "Mesh", filesPrefix, ".obj", FilesLocation, ReplaceMesh, assetType, filesPrefix);
// Textures replacement UI
GUI.Label(new Rect(180, 230, 480, 28), "<b>Textures Replacement</b>");
RealtimeUtils.UIReplace<string, string, TexturesReplacement>(new Vector2(20, 260), "Diffuse", "Diffuse", filesPrefix, "_d.png", FilesLocation, CallReplaceTextures, assetType, filesPrefix, TexturesReplacement.Diffuse);
RealtimeUtils.UIReplace<string, string, TexturesReplacement>(new Vector2(20, 290), "Alpha", "Alpha", filesPrefix, "_a.png", FilesLocation, CallReplaceTextures, assetType, filesPrefix, TexturesReplacement.Alpha);
RealtimeUtils.UIReplace<string, string, TexturesReplacement>(new Vector2(20, 320), "Normal", "Normal", filesPrefix, "_n.png", FilesLocation, CallReplaceTextures, assetType, filesPrefix, TexturesReplacement.Normal);
RealtimeUtils.UIReplace<string, string, TexturesReplacement>(new Vector2(20, 410), "Color", "Color", filesPrefix, "_c.png", FilesLocation, CallReplaceTextures, assetType, filesPrefix, TexturesReplacement.Color);
if (assetType == "tree")
{
RealtimeUtils.UIReplaceUnavailable(new Vector2(20, 380), "Illumination", "trees");
RealtimeUtils.UIReplaceUnavailable(new Vector2(20, 350), "Specular", "trees");
}
else
{
RealtimeUtils.UIReplace<string, string, TexturesReplacement>(new Vector2(20, 380), "Illumination", "Illum.", filesPrefix, "_i.png", FilesLocation, CallReplaceTextures, assetType, filesPrefix, TexturesReplacement.Illumination);
RealtimeUtils.UIReplace<string, string, TexturesReplacement>(new Vector2(20, 350), "Specular", "Specular", filesPrefix, "_s.png", FilesLocation, CallReplaceTextures, assetType, filesPrefix, TexturesReplacement.Specular);
}
if (RealtimeUtils.AnyFileExists(FilesLocation, filesPrefix, RealtimeUtils.AllFilesExtensions(assetType)))
{
if (GUI.Button(new Rect(10, 460, 480, 60), "Replace every existing file"))
{
if (assetType == "building")
ReplaceEverything<BuildingInfo>(selectedBuilding, filesPrefix);
else if (assetType == "prop")
ReplaceEverything<PropInfo>(selectedProp, filesPrefix);
else if (assetType == "tree")
ReplaceEverything<TreeInfo>(selectedTree, filesPrefix);
else if (assetType == "vehicle")
ReplaceEverything<VehicleInfo>(selectedVehicle, filesPrefix);
}
}
else
{
GUI.color = Color.red;
GUI.Label(new Rect(10, 460, 480, 60), "[NO FILE FOUND]", GUI.skin.button);
GUI.color = Color.white;
}
if (GUI.Button(new Rect(10, 525, 480, 28), "Open Files Folder"))
Application.OpenURL("file://" + FilesLocation);
if (assetType == "building")
{
if (selectedBuilding.m_subBuildings.Count() > 0)
{
if (GUI.Button(new Rect(10, 560, 237, 28), "Sub-Buildings"))
scrollingThroughSubBuildings = true;
if (GUI.Button(new Rect(253, 560, 237, 28), "← Back"))
ResetSelection();
}
else
{
if (GUI.Button(new Rect(10, 560, 480, 28), "← Back"))
ResetSelection();
}
}
else
{
if (GUI.Button(new Rect(10, 555, 480, 28), "← Back"))
ResetSelection();
}
}
}
}
}
// Private methods
private void SearchPrefabs(string searchText)
{
if (searchText == string.Empty || searchText == "")
return;
string criteria = searchText.ToLower().Replace(" ", "");
searchedProps = new List<PropInfo>();
searchedVehicles = new List<VehicleInfo>();
searchedTrees = new List<TreeInfo>();
searchedBuildings = new List<BuildingInfo>();
foreach (PropInfo prop in Resources.FindObjectsOfTypeAll<PropInfo>()
.Where(p => p.GetLocalizedTitle().ToLower().Replace("PROPS_TITLE[", "").Replace("]:0", "").Replace(" ", "").Contains(criteria) || p.name.ToLower().Replace(" ", "").Contains(criteria)))
{
searchedProps.Add(prop);
}
foreach (BuildingInfo building in Resources.FindObjectsOfTypeAll<BuildingInfo>()
.Where(p => p.GetLocalizedTitle().ToLower().Replace("BUILDING_TITLE[", "").Replace("]:0", "").Replace(" ", "").Contains(criteria) || p.name.ToLower().Replace(" ", "").Contains(criteria)))
{
searchedBuildings.Add(building);
}
foreach (TreeInfo tree in Resources.FindObjectsOfTypeAll<TreeInfo>()
.Where(p => p.GetLocalizedTitle().ToLower().Replace(" ", "").Contains(criteria) || p.name.ToLower().Replace(" ", "").Contains(criteria)))
{
searchedTrees.Add(tree);
}
foreach (VehicleInfo vehicle in Resources.FindObjectsOfTypeAll<VehicleInfo>()
.Where(p => p.GetLocalizedTitle().ToLower().Replace(" ", "").Contains(criteria) || p.name.ToLower().Replace(" ", "").Contains(criteria)))
{
searchedVehicles.Add(vehicle);
}
}
private bool TryDropPrefab()
{
switch (ToolsModifierControl.toolController.CurrentTool.GetType().ToString())
{
case "PropTool":
assetType = "prop";
selectedProp = (ToolsModifierControl.toolController.CurrentTool as PropTool).m_prefab;
filesPrefix = selectedProp.name.ExtractFilename();
return true;
case "BuildingTool":
assetType = "building";
selectedBuilding = (ToolsModifierControl.toolController.CurrentTool as BuildingTool).m_prefab;
filesPrefix = selectedBuilding.name.ExtractFilename();
return true;
case "TreeTool":
assetType = "tree";
selectedTree = (ToolsModifierControl.toolController.CurrentTool as TreeTool).m_prefab;
filesPrefix = selectedTree.name.ExtractFilename();
return true;
default:
return false;
}
}
private void ResetSelection()
{
uiMode = true;
assetType = string.Empty;
selectedProp = null;
selectedTree = null;
selectedBuilding = null;
selectedVehicle = null;
}
private void SelectPrefab<T>(T prefabInfo) where T : PrefabInfo
{
if (prefabInfo.GetType() == typeof(PropInfo))
{
assetType = "prop";
selectedProp = prefabInfo as PropInfo;
}
else if (prefabInfo.GetType() == typeof(BuildingInfo))
{
assetType = "building";
selectedBuilding = prefabInfo as BuildingInfo;
}
else if (prefabInfo.GetType() == typeof(TreeInfo))
{
assetType = "tree";
selectedTree = prefabInfo as TreeInfo;
}
else if (prefabInfo.GetType() == typeof(VehicleInfo))
{
assetType = "vehicle";
selectedVehicle = prefabInfo as VehicleInfo;
}
filesPrefix = this.SelectedAsPrefabInfo().name.ExtractFilename();
uiMode = false;
}
private void ReplaceEverything<T>(T prefab, string fileName) where T : PrefabInfo
{
Type assetType = prefab.GetType();
if (assetType == typeof(BuildingInfo))
building(prefab as BuildingInfo, fileName);
else if (assetType == typeof(PropInfo))
prop(prefab as PropInfo, fileName);
else if (assetType == typeof(TreeInfo))
tree(prefab as TreeInfo, fileName);
else if (assetType == typeof(VehicleInfo))
vehicle(prefab as VehicleInfo, fileName);
}
private Material GetMaterialFromSelected()
{
if (assetType == "building")
return selectedBuilding.m_material;
else if (assetType == "prop")
return selectedProp.m_material;
else if (assetType == "tree")
return selectedTree.m_material;
else if (assetType == "vehicle")
return selectedVehicle.m_material;
return null;
}
private void ReplaceMesh(string type, string filename)
{
if (type == "building")
{
Mesh replacementMesh;
if (TryGetMesh(out replacementMesh, filename, false)) selectedBuilding.m_mesh = replacementMesh;
}
else if (type == "prop")
{
Mesh replacementMesh;
if (TryGetMesh(out replacementMesh, filename, false)) selectedProp.m_mesh = replacementMesh;
}
else if (type == "tree")
{
Mesh replacementMesh;
if (TryGetMesh(out replacementMesh, filename, false)) selectedTree.m_mesh = replacementMesh;
}
else if (type == "vehicle")
{
Mesh replacementMesh;
if (TryGetMesh(out replacementMesh, filename, true)) selectedVehicle.m_mesh = replacementMesh;
}
}
private void CallReplaceTextures(string type, string filename, TexturesReplacement replace)
{
if (type == "building")
{
ReplaceTextures(selectedBuilding.m_material, type, filename, replace);
}
else if (type == "prop")
{
ReplaceTextures(selectedProp.m_material, type, filename, replace);
if (selectedProp.m_isDecal)
ReplaceTextures(selectedProp.m_lodMaterialCombined, type, filename, replace);
}
else if (type == "tree")
{
ReplaceTextures(selectedTree.m_material, type, filename, replace);
}
else if (type == "vehicle")
{
ReplaceTextures(selectedVehicle.m_material, type, filename, replace);
}
}
// Actual logic (Ronyx69)
public static void building(BuildingInfo asset, string filename)
{
Debug.Log("building called");
if (asset != null)
{
ReplaceTextures(asset.m_material, "building", filename, TexturesReplacement.Diffuse);
ReplaceTextures(asset.m_material, "building", filename, TexturesReplacement.Alpha);
ReplaceTextures(asset.m_material, "building", filename, TexturesReplacement.Color);
ReplaceTextures(asset.m_material, "building", filename, TexturesReplacement.Illumination);
ReplaceTextures(asset.m_material, "building", filename, TexturesReplacement.Normal);
ReplaceTextures(asset.m_material, "building", filename, TexturesReplacement.Specular);
Mesh replacementMesh;
if (TryGetMesh(out replacementMesh, filename, false)) asset.m_mesh = replacementMesh;
}
}
public static void prop(PropInfo asset, string filename)
{
Debug.Log("prop called");
if (asset != null)
{
ReplaceTextures(asset.m_material, "prop", filename, TexturesReplacement.Diffuse);
ReplaceTextures(asset.m_material, "prop", filename, TexturesReplacement.Alpha);
ReplaceTextures(asset.m_material, "prop", filename, TexturesReplacement.Color);
ReplaceTextures(asset.m_material, "prop", filename, TexturesReplacement.Illumination);
ReplaceTextures(asset.m_material, "prop", filename, TexturesReplacement.Normal);
ReplaceTextures(asset.m_material, "prop", filename, TexturesReplacement.Specular);
if (asset.m_isDecal)
{
ReplaceTextures(asset.m_lodMaterialCombined, "prop", filename, TexturesReplacement.Diffuse);
ReplaceTextures(asset.m_lodMaterialCombined, "prop", filename, TexturesReplacement.Alpha);
ReplaceTextures(asset.m_lodMaterialCombined, "prop", filename, TexturesReplacement.Color);
ReplaceTextures(asset.m_lodMaterialCombined, "prop", filename, TexturesReplacement.Illumination);
ReplaceTextures(asset.m_lodMaterialCombined, "prop", filename, TexturesReplacement.Normal);
ReplaceTextures(asset.m_lodMaterialCombined, "prop", filename, TexturesReplacement.Specular);
}
Mesh replacementMesh;
if (TryGetMesh(out replacementMesh, filename, false)) asset.m_mesh = replacementMesh;
}
}
public static void vehicle(VehicleInfo asset, string filename)
{
Debug.Log("vehicle called");
if (asset != null)
{
ReplaceTextures(asset.m_material, "vehicle", filename, TexturesReplacement.Diffuse);
ReplaceTextures(asset.m_material, "vehicle", filename, TexturesReplacement.Alpha);
ReplaceTextures(asset.m_material, "vehicle", filename, TexturesReplacement.Color);
ReplaceTextures(asset.m_material, "vehicle", filename, TexturesReplacement.Illumination);
ReplaceTextures(asset.m_material, "vehicle", filename, TexturesReplacement.Normal);
ReplaceTextures(asset.m_material, "vehicle", filename, TexturesReplacement.Specular);
Mesh replacementMesh;
if (TryGetMesh(out replacementMesh, filename, true)) asset.m_mesh = replacementMesh;
}
}
public static void tree(TreeInfo asset, string filename)
{
Debug.Log("tree called");
if (asset != null)
{
ReplaceTextures(asset.m_material, "tree", filename, TexturesReplacement.Diffuse);
ReplaceTextures(asset.m_renderMaterial, "tree", filename, TexturesReplacement.Diffuse);
ReplaceTextures(asset.m_material, "tree", filename, TexturesReplacement.Normal);
ReplaceTextures(asset.m_renderMaterial, "tree", filename, TexturesReplacement.Normal);
ReplaceTextures(asset.m_material, "tree", filename, TexturesReplacement.Color);
ReplaceTextures(asset.m_renderMaterial, "tree", filename, TexturesReplacement.Color);
ReplaceTextures(asset.m_material, "tree", filename, TexturesReplacement.Alpha);
ReplaceTextures(asset.m_renderMaterial, "tree", filename, TexturesReplacement.Alpha);
Mesh replacementMesh;
if (TryGetMesh(out replacementMesh, filename, false)) asset.m_mesh = replacementMesh;
}
}
public static void ReplaceTextures(Material material, string type, string filename, TexturesReplacement replace)
{
Texture2D textureD = null, textureA = null, textureC = null, textureI = null, textureN = null, textureS = null;
Texture2D providedTexture = null; Color px; var texturePath = " ";
bool _d, _a, _c, _i, _n, _s, aci, xys, xyca, gamma;
_d = _a = _c = _i = _n = _s = aci = xys = xyca = gamma = false;
var path = ColossalFramework.IO.DataLocation.addonsPath + "\\Import\\" + filename;
if (type == "tree")
{
var defaultX = 0.5f; var defaultY = 0.5f; var defaultC = 1f; var defaultA = 1f;
if (replace == TexturesReplacement.Diffuse) // _d (Diffuse)
{
textureD = new Texture2D(1, 1);
texturePath = path + "_d.png";
Debug.Log(texturePath);
if (File.Exists(texturePath))
{
textureD.LoadImage(File.ReadAllBytes(texturePath));
_d = true;
}
if (_d) material.SetTexture("_MainTex", textureD);
}
if (replace == TexturesReplacement.Normal) // _n (Normal)
{
textureN = new Texture2D(1, 1);
texturePath = path + "_n.png";
if (File.Exists(texturePath))
{
textureN.LoadImage(File.ReadAllBytes(texturePath));
providedTexture = textureN; _n = true;
}
}
if (replace == TexturesReplacement.Color) // _c (Color)
{
textureC = new Texture2D(1, 1);
texturePath = path + "_c.png";
if (File.Exists(texturePath))
{
textureC.LoadImage(File.ReadAllBytes(texturePath));
providedTexture = textureC; _c = true;
}
}
if (replace == TexturesReplacement.Alpha) // _a (Alpha)
{
textureA = new Texture2D(1, 1);
texturePath = path + "_a.png";
if (File.Exists(texturePath))
{
textureA.LoadImage(File.ReadAllBytes(texturePath));
providedTexture = textureA; _a = true;
}
}
if (replace == TexturesReplacement.Normal || replace == TexturesReplacement.Color || replace == TexturesReplacement.Alpha) // previous XYCA
{
if (material.GetTexture("_XYCAMap"))
{
var preXYCA = material.GetTexture("_XYCAMap") as Texture2D;
if (providedTexture) providedTexture = new Texture2D(providedTexture.width, providedTexture.height);
else providedTexture = new Texture2D(preXYCA.width, preXYCA.height);
if (preXYCA.name != "replaced")
{
gamma = true;
}
providedTexture.SetPixels(preXYCA.GetPixels());
providedTexture.anisoLevel = preXYCA.anisoLevel;
providedTexture.mipMapBias = preXYCA.mipMapBias;
providedTexture.filterMode = preXYCA.filterMode;
providedTexture.wrapMode = preXYCA.wrapMode;
providedTexture.Apply();
xyca = true; GameObject.Destroy(preXYCA);
}
}
if (replace == TexturesReplacement.Normal || replace == TexturesReplacement.Color || replace == TexturesReplacement.Alpha) // XYCA COMBINATION
{
if (providedTexture)
{
for (int i = 0; i < providedTexture.width; i++)
{
for (int j = 0; j < providedTexture.height; j++)
{
px = providedTexture.GetPixel(i, j);
if (_n)
{
px.r = textureN.GetPixel(i, j).r;
px.g = textureN.GetPixel(i, j).g;
}
else if (!xyca)
{
px.r = defaultX;
px.g = defaultY;
}
if (_c) px.b = textureC.GetPixel(i, j).r; else if (!xyca) px.b = defaultC;
if (_a) px.a = textureA.GetPixel(i, j).r; else if (!xyca) px.a = defaultA;
if (_n || gamma) px.r = Mathf.LinearToGammaSpace(px.r);
if (_n || gamma) px.g = Mathf.LinearToGammaSpace(px.g);
if (_c) px.b = 1 - px.b;
if (_c || gamma) px.b = Mathf.LinearToGammaSpace(px.b);
if (_a) px.a = 1 - px.a;
providedTexture.SetPixel(i, j, px);
}
}
providedTexture.Apply(); providedTexture.name = "replaced";
material.SetTexture("_XYCAMap", providedTexture);
}
}
}
else // if building/prop/vehicle
{
var defaultA = 1f; var defaultC = 1f; var defaultI = 0f;
var defaultX = 0.5f; var defaultY = 0.5f; var defaultS = 0f;
if (type == "building") defaultI = 0.75f;
if (type == "vehicle") defaultI = 0.5f;
if (replace == TexturesReplacement.Diffuse) // _d (Diffuse)
{
textureD = new Texture2D(1, 1);
texturePath = path + "_d.png";
Debug.Log(texturePath);
if (File.Exists(texturePath))
{
textureD.LoadImage(File.ReadAllBytes(texturePath));
_d = true;
}
if (_d) material.SetTexture("_MainTex", textureD);
}
// ACI START
if (replace == TexturesReplacement.Alpha) // _a (Alpha)
{
textureA = new Texture2D(1, 1);
texturePath = path + "_a.png";
if (File.Exists(texturePath))
{
textureA.LoadImage(File.ReadAllBytes(texturePath));
providedTexture = textureA; _a = true;
}
}
if (replace == TexturesReplacement.Color) // _c (Color)
{
textureC = new Texture2D(1, 1);
texturePath = path + "_c.png";
if (File.Exists(texturePath))
{
textureC.LoadImage(File.ReadAllBytes(texturePath));
providedTexture = textureC; _c = true;
}
}
if (replace == TexturesReplacement.Illumination) // _i (Illumination)
{
textureI = new Texture2D(1, 1);
texturePath = path + "_i.png";
if (File.Exists(texturePath))
{
textureI.LoadImage(File.ReadAllBytes(texturePath));
providedTexture = textureI; _i = true;
}
}
if (replace == TexturesReplacement.Alpha || replace == TexturesReplacement.Color || replace == TexturesReplacement.Illumination) // previous ACI
{
if (material.GetTexture("_ACIMap"))
{
var preACI = material.GetTexture("_ACIMap") as Texture2D;
if (providedTexture) providedTexture = new Texture2D(providedTexture.width, providedTexture.height);
else providedTexture = new Texture2D(preACI.width, preACI.height);
if (preACI.name != "replaced")
{
gamma = true;
}
providedTexture.SetPixels(preACI.GetPixels());
providedTexture.anisoLevel = preACI.anisoLevel;
providedTexture.mipMapBias = preACI.mipMapBias;
providedTexture.filterMode = preACI.filterMode;
providedTexture.wrapMode = preACI.wrapMode;
providedTexture.Apply();
aci = true; GameObject.Destroy(preACI);
}
}
if (replace == TexturesReplacement.Alpha || replace == TexturesReplacement.Color || replace == TexturesReplacement.Illumination) // ACI COMBINATION
{
if (providedTexture)
{
for (int i = 0; i < providedTexture.width; i++)
{
for (int j = 0; j < providedTexture.height; j++)
{
px = providedTexture.GetPixel(i, j);
if (_a) px.r = textureA.GetPixel(i, j).r;
else if (!aci) px.r = defaultA;
if (_c) px.g = textureC.GetPixel(i, j).g; else if (!aci) px.g = defaultC;
if (_i) px.b = textureI.GetPixel(i, j).b; else if (!aci) px.b = defaultI;
if (_a) px.r = 1 - px.r;
if (gamma || _a) px.r = Mathf.LinearToGammaSpace(px.r);
if (_c) px.g = 1 - px.g;
if (gamma || _c) px.g = Mathf.LinearToGammaSpace(px.g);
if (gamma || _i) px.b = Mathf.LinearToGammaSpace(px.b);
providedTexture.SetPixel(i, j, px);
}
}
providedTexture.Apply(); providedTexture.name = "replaced";
material.SetTexture("_ACIMap", providedTexture);
}
}
// XYS START
providedTexture = null; gamma = false;
if (replace == TexturesReplacement.Normal) // _n (Normal)
{
textureN = new Texture2D(1, 1);
texturePath = path + "_n.png";
if (File.Exists(texturePath))
{
textureN.LoadImage(File.ReadAllBytes(texturePath));
providedTexture = textureN; _n = true;
}
}
if (replace == TexturesReplacement.Specular) // _s (Specular)
{
textureS = new Texture2D(1, 1);
texturePath = path + "_s.png";
if (File.Exists(texturePath))
{
textureS.LoadImage(File.ReadAllBytes(texturePath));
providedTexture = textureS; _s = true;
}
}
if (replace == TexturesReplacement.Normal || replace == TexturesReplacement.Specular) // previous XYS
{
if (material.GetTexture("_XYSMap"))
{
var preXYS = material.GetTexture("_XYSMap") as Texture2D;
if (providedTexture) providedTexture = new Texture2D(providedTexture.width, providedTexture.height);
else providedTexture = new Texture2D(preXYS.width, preXYS.height);
if (preXYS.name != "replaced")
{
gamma = true;
}
providedTexture.SetPixels(preXYS.GetPixels());
providedTexture.anisoLevel = preXYS.anisoLevel;
providedTexture.mipMapBias = preXYS.mipMapBias;
providedTexture.filterMode = preXYS.filterMode;
providedTexture.wrapMode = preXYS.wrapMode;
providedTexture.Apply();
xys = true; GameObject.Destroy(preXYS);
}
}
if (replace == TexturesReplacement.Normal || replace == TexturesReplacement.Specular) // XYS COMBINATION
{
if (providedTexture)
{
for (int i = 0; i < providedTexture.width; i++)
{
for (int j = 0; j < providedTexture.height; j++)
{
px = providedTexture.GetPixel(i, j);
if (_n)
{
px.r = textureN.GetPixel(i, j).r;
px.g = textureN.GetPixel(i, j).g;
}
else if (!xys)
{
px.r = defaultX;
px.g = defaultY;
}
if (_s) px.b = textureS.GetPixel(i, j).b; else if (!xys) px.b = defaultS;
if (_n || gamma) px.r = Mathf.LinearToGammaSpace(px.r);
if (_n || gamma) px.g = Mathf.LinearToGammaSpace(px.g);
if (_s) px.b = 1 - px.b;
if (_s || gamma) px.b = Mathf.LinearToGammaSpace(px.b);
providedTexture.SetPixel(i, j, px);
}
}
providedTexture.Apply(); providedTexture.name = "replaced";
material.SetTexture("_XYSMap", providedTexture);
}
}
}
}
public static bool TryGetMesh(out Mesh newMesh, string filename, bool isVehicle)
{
string path = "none";
if (File.Exists(ColossalFramework.IO.DataLocation.addonsPath + "\\Import\\" + filename + ".obj")) path = ColossalFramework.IO.DataLocation.addonsPath + "\\Import\\" + filename + ".obj";
// else if (File.Exists(ColossalFramework.IO.DataLocation.addonsPath + "\\Import\\" + assetname + ".obj")) path = ColossalFramework.IO.DataLocation.addonsPath + "\\Import\\" + assetname + ".obj";
if (path != "none")
{
Mesh mesh = new Mesh();
using (FileStream fileStream = File.Open(path, FileMode.Open))
{
mesh.LoadOBJ(OBJLoader.LoadOBJ(fileStream));
}
if (isVehicle)
{
Color nc = new Color(0, 0, 255);
Vector3[] vertices = mesh.vertices;
Color[] colors = new Color[vertices.Length];
for (int i = 0; i < vertices.Length; i++) colors[i] = nc;
mesh.colors = colors;
}
newMesh = mesh;
return true;
}
newMesh = null;
return false;
}
}
// Enum
public enum TexturesReplacement { Diffuse, Alpha, Color, Illumination, Normal, Specular }
// Utils Class
public static class RealtimeUtils
{
public static PrefabInfo SelectedAsPrefabInfo(this RealtimeUI logic)
{
switch (logic.assetType)
{
case "prop":
return logic.selectedProp;
case "building":
return logic.selectedBuilding;
case "tree":
return logic.selectedTree;
case "vehicle":
return logic.selectedVehicle;
}
return null;
}
public static string RemoveMissingTranslationString(this string baseString)
{
return baseString.Replace("BUILDING_TITLE[", "").Replace("PROPS_TITLE[", "").Replace("]:0", "");
}
public static void UIReplace<T>(Vector2 position, string title, string shortReplaceButton, string filePrefix, string fileSuffix, string filesDirectory, Action<T> replaceAction, T param1)
{
GUI.Label(new Rect(position.x, position.y + 3, 330, 23), "<b>" + title + ":</b> " + filePrefix + fileSuffix);
if (filePrefix.IsWindowsRestrictedFile())
{
GUI.color = Color.red;
GUI.Label(new Rect(position.x + 340, position.y, 120, 27), "[INVALID NAME]", GUI.skin.button);
GUI.color = Color.white;
}
else if (File.Exists(filesDirectory + filePrefix + fileSuffix))
{
Debug.Log(filesDirectory + filePrefix + fileSuffix);
if (GUI.Button(new Rect(position.x + 340, position.y, 120, 27), "Replace " + shortReplaceButton))
{
replaceAction.Invoke(param1);
}
}
else
{
GUI.color = Color.red;
GUI.Label(new Rect(position.x + 340, position.y, 120, 27), "[FILE MISSING]", GUI.skin.button);
GUI.color = Color.white;
}
}
public static void UIReplace<T1, T2>(Vector2 position, string title, string shortReplaceButton, string filePrefix, string fileSuffix, string filesDirectory, Action<T1, T2> replaceAction, T1 param1, T2 param2)
{
GUI.Label(new Rect(position.x, position.y + 3, 330, 23), "<b>" + title + ":</b> " + filePrefix + fileSuffix);
if (filePrefix.IsWindowsRestrictedFile())
{
GUI.color = Color.red;
GUI.Label(new Rect(position.x + 340, position.y, 120, 27), "[INVALID NAME]", GUI.skin.button);
GUI.color = Color.white;
}
else if (File.Exists(filesDirectory + filePrefix + fileSuffix))
{
Debug.Log(filesDirectory + filePrefix + fileSuffix);
if (GUI.Button(new Rect(position.x + 340, position.y, 120, 27), "Replace " + shortReplaceButton))
{
replaceAction.Invoke(param1, param2);
}
}
else
{
GUI.color = Color.red;
GUI.Label(new Rect(position.x + 340, position.y, 120, 27), "[FILE MISSING]", GUI.skin.button);
GUI.color = Color.white;
}
}
public static void UIReplace<T1, T2, T3>(Vector2 position, string title, string shortReplaceButton, string filePrefix, string fileSuffix, string filesDirectory, Action<T1, T2, T3> replaceAction, T1 param1, T2 param2, T3 param3)
{
GUI.Label(new Rect(position.x, position.y + 3, 330, 23), "<b>" + title + ":</b> " + filePrefix + fileSuffix);
if (filePrefix.IsWindowsRestrictedFile())
{
GUI.color = Color.red;
GUI.Label(new Rect(position.x + 340, position.y, 120, 27), "[INVALID NAME]", GUI.skin.button);
GUI.color = Color.white;
}
else if (File.Exists(filesDirectory + filePrefix + fileSuffix))
{
Debug.Log(filesDirectory + filePrefix + fileSuffix);
if (GUI.Button(new Rect(position.x + 340, position.y, 120, 27), "Replace " + shortReplaceButton))
{
replaceAction.Invoke(param1, param2, param3);
}
}
else
{
GUI.color = Color.red;
GUI.Label(new Rect(position.x + 340, position.y, 120, 27), "[FILE MISSING]", GUI.skin.button);
GUI.color = Color.white;
}
}
public static void UIReplace<T1, T2, T3, T4>(Vector2 position, string title, string shortReplaceButton, string filePrefix, string fileSuffix, string filesDirectory, Action<T1, T2, T3, T4> replaceAction, T1 param1, T2 param2, T3 param3, T4 param4)
{
GUI.Label(new Rect(position.x, position.y + 3, 330, 23), "<b>" + title + ":</b> " + filePrefix + fileSuffix);
if (filePrefix.IsWindowsRestrictedFile())
{
GUI.color = Color.red;
GUI.Label(new Rect(position.x + 340, position.y, 120, 27), "[INVALID NAME]", GUI.skin.button);
GUI.color = Color.white;
}
else if (File.Exists(filesDirectory + filePrefix + fileSuffix))
{
Debug.Log(filesDirectory + filePrefix + fileSuffix);
if (GUI.Button(new Rect(position.x + 340, position.y, 120, 27), "Replace " + shortReplaceButton))
{
replaceAction.Invoke(param1, param2, param3, param4);
}
}
else
{
GUI.color = Color.red;
GUI.Label(new Rect(position.x + 340, position.y, 120, 27), "[FILE MISSING]", GUI.skin.button);
GUI.color = Color.white;
}
}
public static void UIReplaceUnavailable(Vector2 position, string title, string mode)
{
GUI.Label(new Rect(position.x, position.y + 3, 330, 23), "<b>" + title + ":</b> <i>Unavailable for " + mode + "</i>");
}
public static void ResetTool()
{
ToolsModifierControl.toolController.CurrentTool = ToolsModifierControl.GetTool<DefaultTool>();
ToolsModifierControl.SetTool<DefaultTool>();
ToolsModifierControl.mainToolbar.CloseEverything();
}
public static bool IsWindowsRestrictedFile(this string checkString)
{
string str = checkString.ToLower();
return (str == "aux") || (str == "con") || (str == "com1") || (str == "com2") || (str == "com3") || (str == "com4") || (str == "lpt1") || (str == "lpt2") || (str == "lpt3") || (str == "ptn") || (str == "nul");
}
public static bool IsDroppableToolEnabled()
{
Type toolType = ToolsModifierControl.toolController.CurrentTool.GetType();
return ((toolType == typeof(PropTool)) || (toolType == typeof(BuildingTool)) || (toolType == typeof(TreeTool)));
}
public static string PossessionApostrophe(this string baseString)
{
if (baseString.ToLower()[baseString.Length - 1] == 's')
return baseString += "'";
else
return baseString += "'s";
}
public static bool AnyFileExists(string fileLocation, string filePrefix, params string[] filesSuffixes)
{
if (filePrefix.IsWindowsRestrictedFile())
return false;
return filesSuffixes.Any(suffix => File.Exists(fileLocation + filePrefix + suffix));
}
public static string[] AllFilesExtensions(string assetType)
{
if (assetType == "tree")
return new string[] { ".obj", "_d.png", "_a.png", "_n.png", "_c.png" };
else
return new string[] { ".obj", "_d.png", "_a.png", "_n.png", "_s.png", "_i.png", "_c.png" };
}
public static string ExtractFilename(this string fullPrefabname)
{
if (fullPrefabname.Contains("."))
return fullPrefabname.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries)[0];
else
return fullPrefabname;
}
}
}
@dasaqui
Copy link

dasaqui commented Dec 7, 2020

There is a problem with all linux users, linux uses (and maybe also mac) normal slash to separete folders, while windows uses backslashes, so, sentences like
FilesLocation = DataLocation.addonsPath + "\Import\";
fails on linux, so, to correct it, there is a constant on System.IO to address the correct path separator, i can't compile right now, but changing this all mac and linux users will be gratefull tu use your mod.

I think someting like
FilesLocation = DataLocation.addonsPath + Path.PathSeparator + "Import" + Path.PathSeparator;
could do the job

below i show the error message that we (linux users) have, i wish it could be helpful
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment