From 628848cf7d9164d2e9367a5ee249bbefe0500985 Mon Sep 17 00:00:00 2001 From: drewcassidy Date: Fri, 5 Jun 2020 00:29:23 -0700 Subject: [PATCH] More work trying to fix part icons --- .../ConformalDecalIconFixer.cs | 62 ++++++++ Source/ConformalDecals/ConformalDecals.csproj | 1 + .../MaterialModifiers/MaterialProperty.cs | 10 +- .../MaterialPropertyCollection.cs | 145 ++++++++++++------ .../MaterialTextureProperty.cs | 14 +- .../ModuleConformalDecalBase.cs | 109 +++++++------ .../ModuleConformalDecalFlag.cs | 21 +-- .../ModuleConformalDecalGeneric.cs | 20 +-- 8 files changed, 250 insertions(+), 132 deletions(-) create mode 100644 Source/ConformalDecals/ConformalDecalIconFixer.cs diff --git a/Source/ConformalDecals/ConformalDecalIconFixer.cs b/Source/ConformalDecals/ConformalDecalIconFixer.cs new file mode 100644 index 0000000..22f99e4 --- /dev/null +++ b/Source/ConformalDecals/ConformalDecalIconFixer.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace ConformalDecals { + [KSPAddon(KSPAddon.Startup.EditorAny, true)] + public class ConformalDecalIconFixer : MonoBehaviour { + private static readonly List PartNames = new List(); + + public static void QueuePart(string name) { + PartNames.Add(name); + } + + public void Start() { + foreach (var name in PartNames) { + Debug.Log($"Unf*&king decal preview on {name}"); + var partInfo = PartLoader.getPartInfoByName(name); + + if (partInfo == null) { + Debug.Log($"Part {name} not found!"); + continue; + } + + var icon = partInfo.iconPrefab; + + var decalModule = partInfo.partPrefab.FindModuleImplementing(); + + if (partInfo == null) { + Debug.Log($"Part {name} has no decal module!"); + continue; + } + + var frontTransform = Part.FindHeirarchyTransform(icon.transform, decalModule.decalFront); + var backTransform = Part.FindHeirarchyTransform(icon.transform, decalModule.decalBack); + + if (frontTransform == null) { + Debug.Log($"Part {name} has no frontTransform"); + continue; + } + + if (backTransform == null) { + Debug.Log($"Part {name} has no backTransform"); + continue; + } + + Vector2 backScale = default; + if (decalModule.updateBackScale) { + var aspectRatio = decalModule.materialProperties.AspectRatio; + var size = new Vector2(decalModule.scale, decalModule.scale * aspectRatio); + backScale.x = size.x * decalModule.backTextureBaseScale.x; + backScale.y = size.y * decalModule.backTextureBaseScale.y; + Debug.Log($"backscale is {backScale}"); + } + + backTransform.GetComponent().material = decalModule.backMaterial; + + if (decalModule.updateBackScale) { + backTransform.GetComponent().material.SetTextureScale(PropertyIDs._MainTex, backScale); + } + } + } + } +} \ No newline at end of file diff --git a/Source/ConformalDecals/ConformalDecals.csproj b/Source/ConformalDecals/ConformalDecals.csproj index f8c1bdf..43b0a27 100644 --- a/Source/ConformalDecals/ConformalDecals.csproj +++ b/Source/ConformalDecals/ConformalDecals.csproj @@ -50,6 +50,7 @@ + diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs index 629a7e9..61ac6ef 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs @@ -4,7 +4,7 @@ using Object = UnityEngine.Object; namespace ConformalDecals.MaterialModifiers { public abstract class MaterialProperty : ScriptableObject { - public string Name { + public string PropertyName { get => _propertyName; set { _propertyName = value; @@ -18,7 +18,7 @@ namespace ConformalDecals.MaterialModifiers { public virtual void ParseNode(ConfigNode node) { if (node == null) throw new ArgumentNullException("node cannot be null"); - Name = node.GetValue("name"); + PropertyName = node.GetValue("name"); } public abstract void Modify(Material material); @@ -57,10 +57,10 @@ namespace ConformalDecals.MaterialModifiers { } else { if (valueString == null) - throw new FormatException($"Missing {typeof(T)} value for {valueName} in property '{Name}'"); + throw new FormatException($"Missing {typeof(T)} value for {valueName} in property '{PropertyName}'"); if (valueString == string.Empty) - throw new FormatException($"Empty {typeof(T)} value for {valueName} in property '{Name}'"); + throw new FormatException($"Empty {typeof(T)} value for {valueName} in property '{PropertyName}'"); } if (tryParse(valueString, out var value)) { @@ -72,7 +72,7 @@ namespace ConformalDecals.MaterialModifiers { } else { - throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} in property '{Name}' : '{valueString}"); + throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} in property '{PropertyName}' : '{valueString}"); } } } diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs b/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs index 0e1322a..4c56065 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs @@ -1,19 +1,18 @@ using System; using System.Collections.Generic; +using System.Runtime.Serialization; +using UniLinq; using UnityEngine; namespace ConformalDecals.MaterialModifiers { - public class MaterialPropertyCollection : ScriptableObject { + public class MaterialPropertyCollection : ScriptableObject, ISerializationCallbackReceiver { private static readonly int OpacityId = Shader.PropertyToID("_DecalOpacity"); private static readonly int CutoffId = Shader.PropertyToID("_Cutoff"); - public MaterialTextureProperty MainMaterialTextureProperty => _mainTexture; - public Shader DecalShader => _shader; public Material DecalMaterial { get { - Debug.Log($"{_textureMaterialProperties == null}"); if (_decalMaterial == null) { _decalMaterial = new Material(_shader); UpdateMaterial(_decalMaterial); @@ -35,54 +34,108 @@ namespace ConformalDecals.MaterialModifiers { } } - public float AspectRatio { - get { - if (MainMaterialTextureProperty == null) return 1; - return MainMaterialTextureProperty.AspectRatio; + public MaterialTextureProperty MainTexture { + get => _mainTexture; + set { + if (!_materialProperties.ContainsValue(value)) + throw new ArgumentException($"Texture property {value.name} is not part of this property collection."); + + _mainTexture = value; } } - [SerializeField] private Shader _shader; - [SerializeField] private List _materialProperties; - [SerializeField] private List _textureMaterialProperties; - [SerializeField] private MaterialTextureProperty _mainTexture; + public float AspectRatio { + get { + if (MainTexture == null) { + Debug.Log("No main texture specified! returning 1 for aspect ratio"); + return 1; + } + return MainTexture.AspectRatio; + } + } + + [SerializeField] private string[] _serializedIDs; + [SerializeField] private MaterialProperty[] _serializedProperties; + + [SerializeField] private Shader _shader; + [SerializeField] private MaterialTextureProperty _mainTexture; + + private Dictionary _materialProperties; private Material _decalMaterial; private Material _previewMaterial; - public void Initialize() { - _materialProperties = new List(); - _textureMaterialProperties = new List(); + public void OnBeforeSerialize() { + Debug.Log($"Serializing MaterialPropertyCollection {this.GetInstanceID()}"); + if (_materialProperties == null) throw new SerializationException("Tried to serialize an unininitalized MaterialPropertyCollection"); + + _serializedIDs = _materialProperties.Keys.ToArray(); + _serializedProperties = _materialProperties.Values.ToArray(); + } + + public void OnAfterDeserialize() { + Debug.Log($"Deserializing MaterialPropertyCollection {this.GetInstanceID()}"); + if (_serializedIDs == null) throw new SerializationException("ID array is null"); + if (_serializedProperties == null) throw new SerializationException("Property array is null"); + if (_serializedProperties.Length != _serializedIDs.Length) throw new SerializationException("Material property arrays are different lengths."); + + _materialProperties ??= new Dictionary(); + + for (var i = 0; i < _serializedIDs.Length; i++) { + var property = MaterialProperty.Instantiate(_serializedProperties[i]); + Debug.Log($"insantiating {property.GetType().Name} {property.GetInstanceID()}"); + _materialProperties.Add(_serializedIDs[i], property); + + if (property is MaterialTextureProperty textureProperty) { + _mainTexture = textureProperty; + } + } + } + + public void Awake() { + Debug.Log($"MaterialPropertyCollection {this.GetInstanceID()} onAwake"); + _materialProperties ??= new Dictionary(); } public void AddProperty(MaterialProperty property) { if (property == null) throw new ArgumentNullException("Tried to add a null property"); - if (_materialProperties == null || _textureMaterialProperties == null) { - Initialize(); - Debug.LogWarning("Tried to add a property to uninitialized property collection! correcting now."); - } - foreach (var p in _materialProperties) { - if (p.Name == property.Name) { - _materialProperties.Remove(property); - } - } - - _materialProperties.Add(property); + _materialProperties.Add(property.name, property); if (property is MaterialTextureProperty textureProperty) { - foreach (var p in _textureMaterialProperties) { - if (p.Name == textureProperty.Name) { - _textureMaterialProperties.Remove(textureProperty); - } - } - - _textureMaterialProperties.Add(textureProperty); - if (textureProperty.isMain) _mainTexture ??= textureProperty; } } + public void ParseProperty(ConfigNode node) where T : MaterialProperty { + var name = node.GetValue("name"); + if (string.IsNullOrEmpty(name)) throw new ArgumentException("node has no name"); + + Debug.Log($"Parsing material property {name}"); + + T newProperty; + + if (_materialProperties.ContainsKey(name)) { + if (_materialProperties[name] is T property) { + newProperty = property; + property.ParseNode(node); + } + else { + throw new ArgumentException("Material property already exists for {name} but it has a different type"); + } + } + else { + newProperty = MaterialProperty.CreateInstance(); + Debug.Log($"Adding new material property of type {newProperty.GetType().Name} {newProperty.GetInstanceID()}"); + newProperty.ParseNode(node); + _materialProperties.Add(name, newProperty); + } + + if (newProperty is MaterialTextureProperty textureProperty && textureProperty.isMain) { + _mainTexture = textureProperty; + } + } + public void SetShader(string shaderName) { if (string.IsNullOrEmpty(shaderName)) { if (_shader == null) { @@ -107,9 +160,12 @@ namespace ConformalDecals.MaterialModifiers { DecalMaterial.renderQueue = queue; } - public void SetScale(Vector2 scale) { - foreach (var textureProperty in _textureMaterialProperties) { - textureProperty.UpdateScale(DecalMaterial, scale); + public void UpdateScale(Vector2 scale) { + foreach (var entry in _materialProperties) { + if (entry.Value is MaterialTextureProperty textureProperty) { + textureProperty.UpdateScale(DecalMaterial, scale); + textureProperty.UpdateScale(PreviewMaterial, scale); + } } } @@ -125,20 +181,23 @@ namespace ConformalDecals.MaterialModifiers { if (_decalMaterial == null) { _decalMaterial = DecalMaterial; } + else { + UpdateMaterial(_decalMaterial); + } if (_previewMaterial == null) { _previewMaterial = PreviewMaterial; } - - UpdateMaterial(_decalMaterial); - UpdateMaterial(_previewMaterial); + else { + UpdateMaterial(_previewMaterial); + } } public void UpdateMaterial(Material material) { - if (material == null) throw new ArgumentNullException("material cannot be null"); + if (material == null) throw new ArgumentNullException(nameof(material)); - foreach (var property in _materialProperties) { - property.Modify(material); + foreach (var entry in _materialProperties) { + entry.Value.Modify(material); } } } diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs index 3008167..e1c5f08 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs @@ -16,8 +16,16 @@ namespace ConformalDecals.MaterialModifiers { public float AspectRatio { get { - if (texture == null) return 1; - if (!_hasTile || Mathf.Approximately(0, _tileRect.width)) return ((float) texture.height) / ((float) texture.width); + if (texture == null) { + Debug.Log("Returning 1"); + return 1; + } + + if (!_hasTile) { + Debug.Log("Returning texture aspect ratio"); + return ((float) texture.height) / ((float) texture.width); + } + return _tileRect.height / _tileRect.width; } } @@ -35,7 +43,7 @@ namespace ConformalDecals.MaterialModifiers { public override void ParseNode(ConfigNode node) { base.ParseNode(node); - isNormal = ParsePropertyBool(node, "isNormalMap", true, (Name == "_BumpMap") || isNormal); + isNormal = ParsePropertyBool(node, "isNormalMap", true, (PropertyName == "_BumpMap") || isNormal); isMain = ParsePropertyBool(node, "isMain", true, isMain); autoScale = ParsePropertyBool(node, "autoScale", true, autoScale); diff --git a/Source/ConformalDecals/ModuleConformalDecalBase.cs b/Source/ConformalDecals/ModuleConformalDecalBase.cs index 73bb99a..296781e 100644 --- a/Source/ConformalDecals/ModuleConformalDecalBase.cs +++ b/Source/ConformalDecals/ModuleConformalDecalBase.cs @@ -115,28 +115,51 @@ namespace ConformalDecals { if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'."); } - - if (HighLogic.LoadedSceneIsEditor) { - UpdateTweakables(); + // get back material if necessary + if (updateBackScale) { + this.Log("Getting material and base scale for back material"); + var backRenderer = decalBackTransform.GetComponent(); + if (backRenderer == null) { + this.LogError($"Specified decalBack transform {decalBack} has no renderer attached! Setting updateBackScale to false."); + updateBackScale = false; + } + else { + backMaterial = backRenderer.material; + if (backMaterial == null) { + this.LogError($"Specified decalBack transform {decalBack} has a renderer but no material! Setting updateBackScale to false."); + updateBackScale = false; + } + else { + if (backTextureBaseScale == default) backTextureBaseScale = backMaterial.GetTextureScale(PropertyIDs._MainTex); + } + } } - - // setup material properties - if (HighLogic.LoadedSceneIsGame) { - // additional load, in flight or in the editor - materialProperties = ScriptableObject.Instantiate(materialProperties); - } - else { - // first load, so get everything set up - materialProperties = ScriptableObject.CreateInstance(); - materialProperties.Initialize(); - } - + + ConformalDecalIconFixer.QueuePart(part.name); + // set shader materialProperties.SetShader(shader); } catch (Exception e) { this.LogException("Exception parsing partmodule", e); } + + if (HighLogic.LoadedSceneIsGame) { + UpdateMaterials(); + UpdateScale(); + UpdateProjection(); + } + } + + public override void OnAwake() { + base.OnAwake(); + + if (materialProperties == null) { + materialProperties = ScriptableObject.CreateInstance(); + } + else { + materialProperties = ScriptableObject.Instantiate(materialProperties); + } } public override void OnStart(StartState state) { @@ -149,31 +172,33 @@ namespace ConformalDecals { UpdateTweakables(); } + + materialProperties.SetRenderQueue(DecalQueue); - // clone materialProperties and setup queue + UpdateMaterials(); + UpdateScale(); + if (HighLogic.LoadedSceneIsGame) { - materialProperties = ScriptableObject.Instantiate(materialProperties); - materialProperties.SetRenderQueue(DecalQueue); - // set initial attachment state if (part.parent == null) { - _isAttached = false; + OnDetach(); } else { OnAttach(); } } - - UpdateMaterials(); - UpdateScale(); } - public void OnDestroy() { + public virtual void OnDestroy() { + // remove GameEvents GameEvents.onEditorPartEvent.Remove(OnEditorEvent); GameEvents.onVariantApplied.Remove(OnVariantApplied); // remove from preCull delegate Camera.onPreCull -= Render; + + // destroy material properties object + Destroy(materialProperties); } protected void OnSizeTweakEvent(BaseField field, object obj) { @@ -229,7 +254,7 @@ namespace ConformalDecals { // add to preCull delegate Camera.onPreCull += Render; - //UpdateScale(); + UpdateScale(); UpdateTargets(); UpdateProjection(); } @@ -244,13 +269,13 @@ namespace ConformalDecals { // remove from preCull delegate Camera.onPreCull -= Render; - //UpdateScale(); + UpdateScale(); } protected void UpdateScale() { var aspectRatio = materialProperties.AspectRatio; this.Log($"Aspect ratio is {aspectRatio}"); - var size = new Vector2(scale, scale * materialProperties.AspectRatio); + var size = new Vector2(scale, scale * aspectRatio); // update orthogonal matrix scale _orthoMatrix = Matrix4x4.identity; @@ -278,7 +303,7 @@ namespace ConformalDecals { } // update material scale - materialProperties.SetScale(size); + materialProperties.UpdateScale(size); } protected void UpdateMaterials() { @@ -286,26 +311,6 @@ namespace ConformalDecals { _decalMaterial = materialProperties.DecalMaterial; _previewMaterial = materialProperties.PreviewMaterial; - // get back material if necessary - if (updateBackScale) { - this.Log("Getting material and base scale for back material"); - var backRenderer = decalBackTransform.GetComponent(); - if (backRenderer == null) { - this.LogError($"Specified decalBack transform {decalBack} has no renderer attached! Setting updateBackScale to false."); - updateBackScale = false; - } - else { - backMaterial = backRenderer.material; - if (backMaterial == null) { - this.LogError($"Specified decalBack transform {decalBack} has a renderer but no material! Setting updateBackScale to false."); - updateBackScale = false; - } - else { - if (backTextureBaseScale == default) backTextureBaseScale = backMaterial.GetTextureScale(PropertyIDs._MainTex); - } - } - } - if (decalFrontTransform == null) { this.LogError("No reference to decal front transform"); return; @@ -340,22 +345,16 @@ namespace ConformalDecals { if (renderer.gameObject.activeInHierarchy == false) continue; // skip blacklisted shaders - if (ConformalDecalConfig.IsBlacklisted(renderer.material.shader)) { - this.Log($"Skipping blacklisted shader '{renderer.material.shader.name}' in '{renderer.gameObject.name}'"); - continue; - } + if (ConformalDecalConfig.IsBlacklisted(renderer.material.shader)) continue; var meshFilter = renderer.GetComponent(); if (meshFilter == null) continue; // object has a meshRenderer with no filter, invalid var mesh = meshFilter.mesh; if (mesh == null) continue; // object has a null mesh, invalid - this.Log($"Adding target for object {meshFilter.gameObject.name} with the mesh {mesh.name}"); // create new ProjectionTarget to represent the renderer var target = new ProjectionTarget(renderer, mesh, useBaseNormal); - this.Log("done."); - // add the target to the list _targets.Add(target); } diff --git a/Source/ConformalDecals/ModuleConformalDecalFlag.cs b/Source/ConformalDecals/ModuleConformalDecalFlag.cs index 1c53cef..1ad425d 100644 --- a/Source/ConformalDecals/ModuleConformalDecalFlag.cs +++ b/Source/ConformalDecals/ModuleConformalDecalFlag.cs @@ -11,11 +11,11 @@ namespace ConformalDecals { public override void OnLoad(ConfigNode node) { base.OnLoad(node); - if (HighLogic.LoadedSceneIsGame) { - UpdateMaterials(); - UpdateScale(); - UpdateProjection(); + UpdateFlag(EditorLogic.FlagURL != string.Empty ? EditorLogic.FlagURL : HighLogic.CurrentGame.flagURL); + } + else { + UpdateFlag(defaultFlag); } } @@ -23,20 +23,20 @@ namespace ConformalDecals { base.OnStart(state); if (HighLogic.LoadedSceneIsGame) { - UpdateFlag(EditorLogic.FlagURL != string.Empty ? EditorLogic.FlagURL : HighLogic.CurrentGame.flagURL); GameEvents.onMissionFlagSelect.Add(UpdateFlag); } - else { - UpdateFlag(defaultFlag); - } } public override void OnIconCreate() { this.Log("called OnIconCreate"); - OnStart(StartState.None); UpdateScale(); } + public override void OnDestroy() { + GameEvents.onMissionFlagSelect.Remove(UpdateFlag); + base.OnDestroy(); + } + private void UpdateFlag(string flagUrl) { this.Log($"Loading flag texture '{flagUrl}'."); var flagTexture = GameDatabase.Instance.GetTexture(flagUrl, false); @@ -48,9 +48,10 @@ namespace ConformalDecals { if (flagTextureProperty == null) { this.Log("Initializing flag property"); flagTextureProperty = ScriptableObject.CreateInstance(); - flagTextureProperty.Name = "_Decal"; + flagTextureProperty.PropertyName = "_Decal"; flagTextureProperty.isMain = true; materialProperties.AddProperty(flagTextureProperty); + materialProperties.MainTexture = flagTextureProperty; } else { } diff --git a/Source/ConformalDecals/ModuleConformalDecalGeneric.cs b/Source/ConformalDecals/ModuleConformalDecalGeneric.cs index 1025710..5960578 100644 --- a/Source/ConformalDecals/ModuleConformalDecalGeneric.cs +++ b/Source/ConformalDecals/ModuleConformalDecalGeneric.cs @@ -5,41 +5,29 @@ using UnityEngine; namespace ConformalDecals { public class ModuleConformalDecalGeneric : ModuleConformalDecalBase { public override void OnLoad(ConfigNode node) { - base.OnLoad(node); // set shader materialProperties.SetShader(shader); // add texture nodes foreach (var textureNode in node.GetNodes("TEXTURE")) { - var textureProperty = ScriptableObject.CreateInstance(); - textureProperty.ParseNode(textureNode); - materialProperties.AddProperty(textureProperty); + materialProperties.ParseProperty(textureNode); } // add float nodes foreach (var floatNode in node.GetNodes("FLOAT")) { - var floatProperty = ScriptableObject.CreateInstance(); - floatProperty.ParseNode(floatNode); - materialProperties.AddProperty(floatProperty); + materialProperties.ParseProperty(floatNode); } // add color nodes foreach (var colorNode in node.GetNodes("COLOR")) { - var colorProperty = ScriptableObject.CreateInstance(); - colorProperty.ParseNode(colorNode); - materialProperties.AddProperty(colorProperty); + materialProperties.ParseProperty(colorNode); } - if (HighLogic.LoadedSceneIsGame) { - UpdateMaterials(); - UpdateScale(); - UpdateProjection(); - } + base.OnLoad(node); } public override void OnIconCreate() { this.Log("called OnIconCreate"); - OnStart(StartState.None); UpdateScale(); } }