diff --git a/Distribution/Restock/GameData/ReStock/Plugins/Restock.dll b/Distribution/Restock/GameData/ReStock/Plugins/Restock.dll index 822ad7b5..e8b766fc 100644 Binary files a/Distribution/Restock/GameData/ReStock/Plugins/Restock.dll and b/Distribution/Restock/GameData/ReStock/Plugins/Restock.dll differ diff --git a/Source/Restock/MaterialModifiers/ColorPropertyMaterialModifier.cs b/Source/Restock/MaterialModifiers/ColorPropertyMaterialModifier.cs new file mode 100644 index 00000000..5ab6f883 --- /dev/null +++ b/Source/Restock/MaterialModifiers/ColorPropertyMaterialModifier.cs @@ -0,0 +1,22 @@ +using System; +using UnityEngine; + +namespace Restock.MaterialModifiers +{ + public class ColorPropertyMaterialModifier : IMaterialModifier + { + private readonly string propertyName; + private readonly Color color; + + public ColorPropertyMaterialModifier(string propertyName, Color color) + { + this.propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); + this.color = color; + } + + public void Modify(Material material) + { + material.SetColor(propertyName, color); + } + } +} diff --git a/Source/Restock/MaterialModifiers/FloatPropertyMaterialModifier.cs b/Source/Restock/MaterialModifiers/FloatPropertyMaterialModifier.cs new file mode 100644 index 00000000..29d81938 --- /dev/null +++ b/Source/Restock/MaterialModifiers/FloatPropertyMaterialModifier.cs @@ -0,0 +1,22 @@ +using System; +using UnityEngine; + +namespace Restock.MaterialModifiers +{ + public class FloatPropertyMaterialModifier : IMaterialModifier + { + private readonly string propertyName; + private readonly float value; + + public FloatPropertyMaterialModifier(string propertyName, float value) + { + this.propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); + this.value = value; + } + + public void Modify(Material material) + { + material.SetFloat(propertyName, value); + } + } +} diff --git a/Source/Restock/MaterialModifiers/IMaterialModifier.cs b/Source/Restock/MaterialModifiers/IMaterialModifier.cs new file mode 100644 index 00000000..e1a97528 --- /dev/null +++ b/Source/Restock/MaterialModifiers/IMaterialModifier.cs @@ -0,0 +1,10 @@ +using System; +using UnityEngine; + +namespace Restock.MaterialModifiers +{ + public interface IMaterialModifier + { + void Modify(Material material); + } +} diff --git a/Source/Restock/MaterialModifiers/MaterialModifierParser.cs b/Source/Restock/MaterialModifiers/MaterialModifierParser.cs new file mode 100644 index 00000000..72d4f07f --- /dev/null +++ b/Source/Restock/MaterialModifiers/MaterialModifierParser.cs @@ -0,0 +1,62 @@ +using System; +using UnityEngine; + +namespace Restock.MaterialModifiers +{ + public class MaterialModifierParser + { + private readonly string logContext; + + public MaterialModifierParser(string logContext = nameof(MaterialModifierParser)) + { + this.logContext = logContext ?? throw new ArgumentNullException(nameof(logContext)); + } + + public IMaterialModifier Parse(ConfigNode node) + { + string propertyName = node.GetValue("name"); + + if (propertyName == null) + throw new FormatException("name not found, cannot create material modifier"); + else if (propertyName == string.Empty) + throw new FormatException("name is empty, cannot create material modifier"); + + if (node.name == "FLOAT_PROPERTY") + { + float value = float.Parse(node.GetValue("value")); + + return new FloatPropertyMaterialModifier(propertyName, value); + } + else if (node.name == "COLOR_PROPERTY") + { + Color color = ConfigNode.ParseColor(node.GetValue("color")); + + return new ColorPropertyMaterialModifier(propertyName, color); + } + else if (node.name == "TEXTURE_PROPERTY") + { + string textureUrl = node.GetValue("textureUrl"); + bool normalMapToggle = false; + + if (node.GetValue("isNormalMap") is string normalMapToggleString) + normalMapToggle = bool.Parse(normalMapToggleString); + + GameDatabase.TextureInfo textureInfo = GameDatabase.Instance.GetTextureInfo(textureUrl); + + if (textureInfo == null) + throw new Exception($"Cannot find texture: '{textureUrl}'"); + + Texture2D texture = normalMapToggle ? textureInfo.normalMap : textureInfo.texture; + + if (texture == null) + throw new Exception($"Cannot get texture from texture info '{textureUrl}' isNormalMap = {normalMapToggle}"); + + return new TexturePropertyMaterialModifier(propertyName, texture); + } + else + { + throw new FormatException($"Can't create material modifier from node: '{node.name}'"); + } + } + } +} diff --git a/Source/Restock/MaterialModifiers/TexturePropertyMaterialModifier.cs b/Source/Restock/MaterialModifiers/TexturePropertyMaterialModifier.cs new file mode 100644 index 00000000..004763ae --- /dev/null +++ b/Source/Restock/MaterialModifiers/TexturePropertyMaterialModifier.cs @@ -0,0 +1,22 @@ +using System; +using UnityEngine; + +namespace Restock.MaterialModifiers +{ + public class TexturePropertyMaterialModifier : IMaterialModifier + { + private readonly string propertyName; + private readonly Texture2D texture; + + public TexturePropertyMaterialModifier(string propertyName, Texture2D texture) + { + this.propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); + this.texture = texture ?? throw new ArgumentNullException(nameof(texture)); + } + + public void Modify(Material material) + { + material.SetTexture(propertyName, texture); + } + } +} diff --git a/Source/Restock/ModuleRestockModifyFairingMaterials.cs b/Source/Restock/ModuleRestockModifyFairingMaterials.cs index e2e448c4..908dab6a 100644 --- a/Source/Restock/ModuleRestockModifyFairingMaterials.cs +++ b/Source/Restock/ModuleRestockModifyFairingMaterials.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using UnityEngine; +using Restock.MaterialModifiers; namespace Restock { @@ -30,7 +31,7 @@ namespace Restock if (string.IsNullOrEmpty(serializedNode)) { - Debug.LogError("Serialized node is null or empty!"); + this.LogError("Serialized node is null or empty!"); yield break; } @@ -40,39 +41,34 @@ namespace Restock if (fairingModule == null) { - Debug.LogError("No fairing module found on part!"); + this.LogError("No fairing module found on part!"); yield break; } - UpdateMaterial(fairingModule.FairingMaterial, node); - UpdateMaterial(fairingModule.FairingConeMaterial, node); - UpdateMaterial(fairingModule.FairingFlightMaterial, node); - UpdateMaterial(fairingModule.FairingFlightConeMaterial, node); + MaterialModifierParser parser = new MaterialModifierParser(); - foreach (ProceduralFairings.FairingPanel fairingPanel in fairingModule.Panels) - { - MeshRenderer renderer = fairingPanel.go.GetComponent(); - UpdateMaterial(renderer.material, node); - } - } - - private void UpdateMaterial(Material material, ConfigNode node) - { foreach (ConfigNode node2 in node.nodes) { - if (node2.name == "COLOR_PROPERTY") + IMaterialModifier modifier; + try { - string name = node2.GetValue("name"); - Color color = ConfigNode.ParseColor(node2.GetValue("color")); - - material.SetColor(name, color); + modifier = parser.Parse(node2); } - else if (node2.name == "FLOAT_PROPERTY") + catch (Exception ex) { - string name = node2.GetValue("name"); - float value = float.Parse(node2.GetValue("value")); + this.LogException($"cannot parse node as material modifier: \n{node2.ToString()}\n", ex); + continue; + } - material.SetFloat(name, value); + modifier.Modify(fairingModule.FairingMaterial); + modifier.Modify(fairingModule.FairingConeMaterial); + modifier.Modify(fairingModule.FairingFlightMaterial); + modifier.Modify(fairingModule.FairingFlightConeMaterial); + + foreach (ProceduralFairings.FairingPanel fairingPanel in fairingModule.Panels) + { + MeshRenderer renderer = fairingPanel.go.GetComponent(); + modifier.Modify(renderer.material); } } } diff --git a/Source/Restock/ModuleRestockModifyMaterials.cs b/Source/Restock/ModuleRestockModifyMaterials.cs index c91aa9cd..85b827ae 100644 --- a/Source/Restock/ModuleRestockModifyMaterials.cs +++ b/Source/Restock/ModuleRestockModifyMaterials.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; +using Restock.MaterialModifiers; namespace Restock { @@ -11,18 +14,10 @@ namespace Restock if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight) return; - Transform modelTransform = part.partTransform.Find("model"); - - Renderer[] renderers = modelTransform.GetComponentsInChildren(); - - if (modelTransform == null) - { - Debug.LogError("Couldn't find model transform"); - return; - } - foreach (ConfigNode node2 in node.GetNodes("MATERIAL")) { + IEnumerable renderers = GetRenderers(node2); + if (node2.GetValue("shader") is string newShaderName) { if (Shader.Find(newShaderName) is Shader newShader) @@ -34,34 +29,30 @@ namespace Restock } else { - Debug.LogError($"Can't find shader {newShaderName}"); + this.LogError($"Can't find shader {newShaderName}"); continue; } } - foreach (ConfigNode node3 in node2.GetNodes("TEXTURE_PROPERTY")) + MaterialModifierParser parser = new MaterialModifierParser(); + + foreach (ConfigNode node3 in node2.nodes) { - string name = node3.GetValue("name"); - string textureUrl = node3.GetValue("textureUrl"); - bool normalMapToggle = false; - - if (node3.GetValue("isNormalMap") is string normalMapToggleString) + IMaterialModifier modifier; + try { - normalMapToggle = bool.Parse(normalMapToggleString); + modifier = parser.Parse(node3); } - - GameDatabase.TextureInfo textureInfo = GameDatabase.Instance.GetTextureInfo(textureUrl); - - if (textureInfo == null) + catch (Exception ex) { - Debug.LogError($"Cannot find texture: {textureUrl}"); + this.LogException($"cannot parse node as material modifier: \n{node3.ToString()}\n", ex); continue; } foreach (Renderer renderer in renderers) { - renderer.material.SetTexture(name, normalMapToggle ? textureInfo.normalMap : textureInfo.texture); + modifier.Modify(renderer.material); } } } @@ -69,5 +60,73 @@ namespace Restock isEnabled = false; moduleIsEnabled = false; } + + private IEnumerable GetRenderers(ConfigNode node) + { + IEnumerable renderers = Enumerable.Empty(); + bool useAllRenderers = true; + + foreach (ConfigNode.Value value in node.values) + { + if (value.name == "transform") + { + Transform[] modelTransforms = part.FindModelTransforms(value.name); + + if (modelTransforms.Length == 0) + { + this.LogError($"Couldn't find transform named '{value.name}' on part"); + continue; + } + + List transformRenderers = new List(modelTransforms.Length); + foreach (Transform transform in modelTransforms) + { + Renderer renderer = transform.GetComponent(); + + if (renderer == null) + this.LogError($"No renderer found on transform '{transform.name}'"); + else + transformRenderers.Add(renderer); + } + + renderers.Concat(transformRenderers); + useAllRenderers = false; + } + else if (value.name == "baseTransform") + { + Transform[] modelTransforms = part.FindModelTransforms(value.name); + + if (modelTransforms.Length == 0) + { + this.LogError($"Couldn't find transform named '{value.name}' on part"); + continue; + } + + foreach (Transform transform in modelTransforms) + { + Renderer[] transformRenderers = transform.GetComponentsInChildren(); + + if (transformRenderers.Length == 0) + this.LogError($"No renderers found on transform '{transform.name}' or its children"); + else + renderers.Concat(transform.GetComponentsInChildren()); + } + + useAllRenderers = false; + } + } + + if (useAllRenderers) + { + Transform modelTransform = part.partTransform.Find("model"); + + if (modelTransform == null) + this.LogError("Couldn't find model transform"); + else + renderers = modelTransform.GetComponentsInChildren(); + } + + return renderers; + } } } diff --git a/Source/Restock/PartModuleExtensions.cs b/Source/Restock/PartModuleExtensions.cs new file mode 100644 index 00000000..51834e53 --- /dev/null +++ b/Source/Restock/PartModuleExtensions.cs @@ -0,0 +1,16 @@ +using System; +using UnityEngine; + +namespace Restock +{ + public static class PartModuleExtensions + { + public static void Log(this PartModule module, string message) => Debug.Log(FormatMessage(module, message)); + public static void LogWarning(this PartModule module, string message) => Debug.LogWarning(FormatMessage(module, message)); + public static void LogError(this PartModule module, string message) => Debug.LogError(FormatMessage(module, message)); + public static void LogException(this PartModule module, string message, Exception exception) => Debug.LogException(new Exception(FormatMessage(module, message), exception)); + + private static string FormatMessage(PartModule module, string message) => $"[{GetPartName(module.part)} {module.GetType()}] {message}"; + private static string GetPartName(Part part) => part.partInfo?.name ?? part.name; + } +} diff --git a/Source/Restock/Restock.csproj b/Source/Restock/Restock.csproj index edf1073e..d831f31a 100644 --- a/Source/Restock/Restock.csproj +++ b/Source/Restock/Restock.csproj @@ -41,6 +41,11 @@ + + + + +