diff --git a/Distribution/GameData/ConformalDecals/Parts/decal-blank.cfg b/Distribution/GameData/ConformalDecals/Parts/decal-blank.cfg index 289a285..fd0ddc2 100644 --- a/Distribution/GameData/ConformalDecals/Parts/decal-blank.cfg +++ b/Distribution/GameData/ConformalDecals/Parts/decal-blank.cfg @@ -47,24 +47,22 @@ PART decalFront = Decal-Front decalBack = Decal-Back + decalShader = ConformalDecals/Feature/Bumped - MATERIAL - { - shader = ConformalDecals/Feature/Bumped + useBaseNormal = false - TEXTURE - { - name = _Decal - textureURL = ConformalDecals/Assets/Tortilla-diffuse - isMain = true - } + TEXTURE + { + name = _Decal + textureURL = ConformalDecals/Assets/Tortilla-diffuse + isMain = true + } - TEXTURE - { - name = _BumpMap - textureURL = ConformalDecals/Assets/Tortilla-normal - isNormalMap = true - } + TEXTURE + { + name = _BumpMap + textureURL = ConformalDecals/Assets/Tortilla-normal + isNormalMap = true } } } diff --git a/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll index 0da29e5..5ec3080 100644 Binary files a/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll and b/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll differ diff --git a/Source/ConformalDecals/ConformalDecals.csproj b/Source/ConformalDecals/ConformalDecals.csproj index 2109274..08a2aee 100644 --- a/Source/ConformalDecals/ConformalDecals.csproj +++ b/Source/ConformalDecals/ConformalDecals.csproj @@ -49,11 +49,11 @@ - - + + - + diff --git a/Source/ConformalDecals/MaterialModifiers/ColorMaterialProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs similarity index 72% rename from Source/ConformalDecals/MaterialModifiers/ColorMaterialProperty.cs rename to Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs index ddd6241..ab0ff88 100644 --- a/Source/ConformalDecals/MaterialModifiers/ColorMaterialProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs @@ -2,10 +2,10 @@ using System; using UnityEngine; namespace ConformalDecals.MaterialModifiers { - public class ColorMaterialProperty : MaterialProperty { + public class MaterialColorProperty : MaterialProperty { private readonly Color _color; - public ColorMaterialProperty(ConfigNode node) : base(node) { + public MaterialColorProperty(ConfigNode node) : base(node) { _color = ParsePropertyColor(node, "color", false); } diff --git a/Source/ConformalDecals/MaterialModifiers/FloatMaterialProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs similarity index 72% rename from Source/ConformalDecals/MaterialModifiers/FloatMaterialProperty.cs rename to Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs index 2154eb2..6efb83b 100644 --- a/Source/ConformalDecals/MaterialModifiers/FloatMaterialProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs @@ -2,10 +2,10 @@ using System; using UnityEngine; namespace ConformalDecals.MaterialModifiers { - public class FloatMaterialProperty : MaterialProperty { + public class MaterialFloatProperty : MaterialProperty { private readonly float _value; - public FloatMaterialProperty(ConfigNode node) : base(node) { + public MaterialFloatProperty(ConfigNode node) : base(node) { _value = ParsePropertyFloat(node, "value", false); } diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs b/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs index 360262a..0b0397e 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs @@ -1,22 +1,16 @@ using System; using System.Collections.Generic; -using Smooth.Delegates; using UnityEngine; namespace ConformalDecals.MaterialModifiers { public class MaterialPropertyCollection : ScriptableObject { - private static int _opacityID = Shader.PropertyToID("_Opacity"); - private static int _cutoffID = Shader.PropertyToID("_Cutoff"); + private static readonly int OpacityId = Shader.PropertyToID("_Opacity"); + private static readonly int CutoffId = Shader.PropertyToID("_Cutoff"); - public TextureMaterialProperty MainTextureProperty { get; set; } - - public bool UseBaseNormal { get; private set; } + public MaterialTextureProperty MainMaterialTextureProperty { get; set; } private List _materialProperties; - private List _textureMaterialProperties; - - public String BaseNormalSrc { get; private set; } - public String BaseNormalDest { get; private set; } + private List _textureMaterialProperties; public Material DecalMaterial { get { @@ -28,107 +22,60 @@ namespace ConformalDecals.MaterialModifiers { } } - private Shader _decalShader; + private Shader _decalShader; private Material _protoDecalMaterial; - private const string _normalTextureName = "_BumpMap"; - - public void Initialize(ConfigNode node, PartModule module) { - - // Initialize fields + public void Initialize() { _materialProperties = new List(); - _textureMaterialProperties = new List(); - - // Get shader - var shaderString = node.GetValue("shader") ?? throw new FormatException("Missing shader name in material"); - - _decalShader = Shabby.Shabby.FindShader(shaderString); + _textureMaterialProperties = new List(); + } - // note to self: null coalescing does not work on UnityEngine classes - if (_decalShader == null) { - throw new FormatException($"Shader not found: '{shaderString}'"); + public void AddProperty(MaterialProperty property) { + foreach (var p in _materialProperties) { + if (p.PropertyName == property.PropertyName) { + _materialProperties.Remove(property); + } } - // Get useBaseNormal value - var useBaseNormalString = node.GetValue("useBaseNormal"); + _materialProperties.Add(property); - if (useBaseNormalString != null) { - if (bool.TryParse(useBaseNormalString, out var useBaseNormalRef)) { - UseBaseNormal = useBaseNormalRef; - } - else { - throw new FormatException($"Improperly formatted bool value for 'useBaseNormal' : {useBaseNormalString}"); + if (property is MaterialTextureProperty textureProperty) { + foreach (var p in _textureMaterialProperties) { + if (p.PropertyName == textureProperty.PropertyName) { + _textureMaterialProperties.Remove(textureProperty); + } } - } - else { - UseBaseNormal = false; - } - // Get basenormal source and destination property names - BaseNormalSrc = node.GetValue("baseNormalSource") ?? _normalTextureName; - BaseNormalDest = node.GetValue("baseNormalDestination") ?? _normalTextureName; - - // Parse all materialProperties - foreach (ConfigNode propertyNode in node.nodes) { - try { - MaterialProperty property; - switch (propertyNode.name) { - case "FLOAT": - property = new FloatMaterialProperty(propertyNode); - break; - - case "COLOR": - property = new ColorMaterialProperty(propertyNode); - break; - - case "TEXTURE": - property = new TextureMaterialProperty(propertyNode); - var textureModifier = (TextureMaterialProperty) property; - if (textureModifier.IsMain) { - if (MainTextureProperty == null) { - MainTextureProperty = textureModifier; - } - else { - // multiple textures have been marked as main! - // non-fatal issue, ignore this one and keep using current main texture - module.LogWarning( - $"Material texture property {textureModifier.TextureUrl} is marked as main, but material already has a main texture! \n" + - $"Defaulting to {MainTextureProperty.TextureUrl}"); - } - } - - _textureMaterialProperties.Add(textureModifier); - break; - - default: - throw new FormatException($"Invalid property type '{propertyNode.name}' in material"); - } + _textureMaterialProperties.Add(textureProperty); - _materialProperties.Add(property); - } + if (textureProperty.IsMain) MainMaterialTextureProperty ??= textureProperty; + } + } - catch (Exception e) { - // Catch exception from parsing current material property - // And print it to the log as an Error - module.LogException("Exception while parsing material node", e); - } + public void SetShader(string shaderName) { + if (_decalShader == null && string.IsNullOrEmpty(shaderName)) { + throw new FormatException("Shader name not provided"); } - module.Log($"Parsed {_materialProperties.Count} properties"); + var shader = Shabby.Shabby.FindShader(shaderName); + + if (shader == null) throw new FormatException($"Unable to find specified shader '{shaderName}'"); + + _decalShader = shader; } - + public void SetScale(Material material, Vector2 scale) { foreach (var textureProperty in _textureMaterialProperties) { textureProperty.UpdateScale(material, scale); } } - + public void SetOpacity(Material material, float opacity) { - material.SetFloat(_opacityID, opacity); + material.SetFloat(OpacityId, opacity); } public void SetCutoff(Material material, float cutoff) { - material.SetFloat(_cutoffID, cutoff); + material.SetFloat(CutoffId, cutoff); } private Material MakeMaterial(Shader shader) { diff --git a/Source/ConformalDecals/MaterialModifiers/TextureMaterialProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs similarity index 94% rename from Source/ConformalDecals/MaterialModifiers/TextureMaterialProperty.cs rename to Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs index 759f5b6..d327b65 100644 --- a/Source/ConformalDecals/MaterialModifiers/TextureMaterialProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs @@ -2,7 +2,7 @@ using System; using UnityEngine; namespace ConformalDecals.MaterialModifiers { - public class TextureMaterialProperty : MaterialProperty { + public class MaterialTextureProperty : MaterialProperty { public string TextureUrl { get; } public Texture2D TextureRef { get; } @@ -17,7 +17,7 @@ namespace ConformalDecals.MaterialModifiers { private readonly Vector2 _textureOffset; private readonly Vector2 _textureScale; - public TextureMaterialProperty(ConfigNode node) : base(node) { + public MaterialTextureProperty(ConfigNode node) : base(node) { TextureUrl = node.GetValue("textureURL"); var textureInfo = GameDatabase.Instance.GetTextureInfo(TextureUrl); diff --git a/Source/ConformalDecals/ModuleConformalDecal.cs b/Source/ConformalDecals/ModuleConformalDecal.cs index 6c48831..2b341b3 100644 --- a/Source/ConformalDecals/ModuleConformalDecal.cs +++ b/Source/ConformalDecals/ModuleConformalDecal.cs @@ -28,6 +28,7 @@ namespace ConformalDecals { [KSPField] public string decalBack = string.Empty; [KSPField] public string decalModel = string.Empty; [KSPField] public string decalProjector = string.Empty; + [KSPField] public string decalShader = string.Empty; [KSPField] public Transform decalFrontTransform; [KSPField] public Transform decalBackTransform; @@ -46,9 +47,12 @@ namespace ConformalDecals { [KSPField] public Vector2 decalQueueRange = new Vector2(2100, 2400); [KSPField] public bool updateBackScale = true; + [KSPField] public bool useBaseNormal = true; [KSPField] public MaterialPropertyCollection materialProperties; - [KSPField] public Material decalMaterial; + + [KSPField] public Material decalMaterial; + [KSPField] public Material backMaterial; private static int _decalQueueCounter = -1; @@ -59,7 +63,11 @@ namespace ConformalDecals { private Bounds _decalBounds; private Vector2 _backTextureBaseScale; - private Material _backMaterial; + public ModuleConformalDecal() { + decalBackTransform = null; + decalModelTransform = null; + decalProjectorTransform = null; + } private int DecalQueue { get { @@ -76,16 +84,40 @@ namespace ConformalDecals { public override void OnLoad(ConfigNode node) { this.Log("Loading module"); try { - // parse MATERIAL node - var materialNode = node.GetNode("MATERIAL") ?? throw new FormatException("Missing MATERIAL node in module"); - materialProperties = ScriptableObject.CreateInstance(); - materialProperties.Initialize(materialNode, this); + + if (materialProperties == null) { + // materialProperties is null, so make a new one + materialProperties = ScriptableObject.CreateInstance(); + materialProperties.Initialize(); + } + else { + // materialProperties already exists, so make a copy + materialProperties = ScriptableObject.Instantiate(materialProperties); + } + + // add texture nodes + foreach (var textureNode in node.GetNodes("TEXTURE")) { + materialProperties.AddProperty(new MaterialTextureProperty(textureNode)); + } + + // add float nodes + foreach (var floatNode in node.GetNodes("FLOAT")) { + materialProperties.AddProperty(new MaterialFloatProperty(floatNode)); + } + + // add color nodes + foreach (var colorNode in node.GetNodes("COLOR")) { + materialProperties.AddProperty(new MaterialColorProperty(colorNode)); + } + + // set shader + materialProperties.SetShader(decalShader); // get decal material decalMaterial = materialProperties.DecalMaterial; // get aspect ratio from main texture, if it exists - var mainTexture = materialProperties.MainTextureProperty; + var mainTexture = materialProperties.MainMaterialTextureProperty; if (mainTexture != null) { aspectRatio = mainTexture.AspectRatio; } @@ -127,6 +159,12 @@ namespace ConformalDecals { decalProjectorTransform = part.FindModelTransform(decalProjector); if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'."); } + + // update EVERYTHING if currently attached + if (_isAttached) { + OnDetach(); + OnAttach(); + } } catch (Exception e) { this.LogException("Exception parsing partmodule", e); @@ -207,12 +245,12 @@ namespace ConformalDecals { this.LogError($"Specified decalBack transform {decalBack} has no renderer attached! Setting updateBackScale to false."); updateBackScale = false; } - else if ((_backMaterial = backRenderer.material) == null) { + else if ((backMaterial = backRenderer.material) == null) { this.LogError($"Specified decalBack transform {decalBack} has a renderer but no material! Setting updateBackScale to false."); updateBackScale = false; } else { - _backTextureBaseScale = _backMaterial.GetTextureScale(PropertyIDs._MainTex); + _backTextureBaseScale = backMaterial.GetTextureScale(PropertyIDs._MainTex); } } @@ -300,7 +338,7 @@ namespace ConformalDecals { 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, materialProperties); + var target = new ProjectionTarget(renderer, mesh, useBaseNormal); this.Log("done."); @@ -349,7 +387,7 @@ namespace ConformalDecals { // update back material scale if (updateBackScale) { - _backMaterial.SetTextureScale(PropertyIDs._MainTex, new Vector2(size.x * _backTextureBaseScale.x, size.y * _backTextureBaseScale.y)); + backMaterial.SetTextureScale(PropertyIDs._MainTex, new Vector2(size.x * _backTextureBaseScale.x, size.y * _backTextureBaseScale.y)); } // update material scale diff --git a/Source/ConformalDecals/ProjectionTarget.cs b/Source/ConformalDecals/ProjectionTarget.cs index f94dc01..8829123 100644 --- a/Source/ConformalDecals/ProjectionTarget.cs +++ b/Source/ConformalDecals/ProjectionTarget.cs @@ -17,30 +17,29 @@ namespace ConformalDecals { private bool _projectionEnabled; // property block - public readonly MaterialPropertyBlock decalMPB; + private readonly MaterialPropertyBlock _decalMPB; - public ProjectionTarget(MeshRenderer targetRenderer, Mesh targetMesh, MaterialPropertyCollection properties) { + public ProjectionTarget(MeshRenderer targetRenderer, Mesh targetMesh, bool useBaseNormal) { target = targetRenderer.transform; _targetRenderer = targetRenderer; _targetMesh = targetMesh; var targetMaterial = targetRenderer.sharedMaterial; - decalMPB = new MaterialPropertyBlock(); + _decalMPB = new MaterialPropertyBlock(); - if (properties.UseBaseNormal) { - var normalSrcID = Shader.PropertyToID(properties.BaseNormalSrc); - var normalDestID = Shader.PropertyToID(properties.BaseNormalDest); - var normalDestIDST = Shader.PropertyToID(properties.BaseNormalDest + "_ST"); + if (useBaseNormal) { + var normalID = Shader.PropertyToID("_BumpMap"); + var normalIDST = Shader.PropertyToID("_BumpMap_ST"); - var normal = targetMaterial.GetTexture(normalSrcID); + var normal = targetMaterial.GetTexture(normalID); if (normal != null) { - decalMPB.SetTexture(normalDestID, targetMaterial.GetTexture(normalSrcID)); + _decalMPB.SetTexture(normalID, targetMaterial.GetTexture(normalID)); - var normalScale = targetMaterial.GetTextureScale(normalSrcID); - var normalOffset = targetMaterial.GetTextureOffset(normalSrcID); + var normalScale = targetMaterial.GetTextureScale(normalID); + var normalOffset = targetMaterial.GetTextureOffset(normalID); - decalMPB.SetVector(normalDestIDST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y)); + _decalMPB.SetVector(normalIDST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y)); } } } @@ -55,9 +54,9 @@ namespace ConformalDecals { var decalNormal = projectorToTargetMatrix.MultiplyVector(Vector3.back).normalized; var decalTangent = projectorToTargetMatrix.MultiplyVector(Vector3.right).normalized; - decalMPB.SetMatrix(_projectionMatrixID, projectionMatrix); - decalMPB.SetVector(_decalNormalID, decalNormal); - decalMPB.SetVector(_decalTangentID, decalTangent); + _decalMPB.SetMatrix(_projectionMatrixID, projectionMatrix); + _decalMPB.SetVector(_decalNormalID, decalNormal); + _decalMPB.SetVector(_decalTangentID, decalTangent); Debug.Log($"Projection enabled for {target.gameObject}"); } else { @@ -68,10 +67,10 @@ namespace ConformalDecals { public bool Render(Material decalMaterial, MaterialPropertyBlock partMPB, Camera camera) { if (_projectionEnabled) { - decalMPB.SetFloat(PropertyIDs._RimFalloff, partMPB.GetFloat(PropertyIDs._RimFalloff)); - decalMPB.SetColor(PropertyIDs._RimColor, partMPB.GetColor(PropertyIDs._RimColor)); + _decalMPB.SetFloat(PropertyIDs._RimFalloff, partMPB.GetFloat(PropertyIDs._RimFalloff)); + _decalMPB.SetColor(PropertyIDs._RimColor, partMPB.GetColor(PropertyIDs._RimColor)); - Graphics.DrawMesh(_targetMesh, target.localToWorldMatrix, decalMaterial, 0, camera, 0, decalMPB, ShadowCastingMode.Off, true); + Graphics.DrawMesh(_targetMesh, target.localToWorldMatrix, decalMaterial, 0, camera, 0, _decalMPB, ShadowCastingMode.Off, true); return true; }