From 6a5604911d067d16f1bb55d114b7e6d2eea9657b Mon Sep 17 00:00:00 2001 From: drewcassidy Date: Tue, 2 Jun 2020 20:36:45 -0700 Subject: [PATCH] Major refactor, splitting ModuleConformalDecal --- .../Plugins/ConformalDecals.dll | Bin 29184 -> 29696 bytes Source/ConformalDecals/ConformalDecals.csproj | 4 +- .../MaterialPropertyCollection.cs | 47 +++++---- .../MaterialTextureProperty.cs | 38 ++++---- ...alDecal.cs => ModuleConformalDecalBase.cs} | 90 ++++++------------ .../ModuleConformalDecalFlag.cs | 32 +++++++ .../ModuleConformalDecalGeneric.cs | 26 +++++ 7 files changed, 137 insertions(+), 100 deletions(-) rename Source/ConformalDecals/{ModuleConformalDecal.cs => ModuleConformalDecalBase.cs} (83%) create mode 100644 Source/ConformalDecals/ModuleConformalDecalFlag.cs create mode 100644 Source/ConformalDecals/ModuleConformalDecalGeneric.cs diff --git a/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll index a715ca64aaa7188496de327e5aa738f4ce55aaa1..10325b1a0f80c99395a566c056691b67bc6b63b0 100644 GIT binary patch literal 29696 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⩔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 + + - + diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs b/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs index ed9f3ba..8f80426 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs @@ -14,16 +14,22 @@ namespace ConformalDecals.MaterialModifiers { public Material DecalMaterial { get { - if (_protoDecalMaterial == null) { - _protoDecalMaterial = MakeMaterial(_decalShader); + if (_decalMaterial == null) { + _decalMaterial = new Material(_decalShader); + UpdateMaterial(_decalMaterial); } - return _protoDecalMaterial; + return _decalMaterial; } } - private Shader _decalShader; - private Material _protoDecalMaterial; + public Shader DecalShader => _decalShader; + + public float AspectRatio => MainMaterialTextureProperty?.AspectRatio ?? 1f; + + [SerializeField] private Shader _decalShader; + + private Material _decalMaterial; public void Initialize() { _materialProperties = new List(); @@ -36,7 +42,7 @@ namespace ConformalDecals.MaterialModifiers { Initialize(); Debug.LogWarning("Tried to add a property to uninitialized property collection! correcting now."); } - + foreach (var p in _materialProperties) { if (p.PropertyName == property.PropertyName) { _materialProperties.Remove(property); @@ -70,33 +76,38 @@ namespace ConformalDecals.MaterialModifiers { } 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) { + public void SetRenderQueue(int queue) { + DecalMaterial.renderQueue = queue; + } + + public void SetScale(Vector2 scale) { foreach (var textureProperty in _textureMaterialProperties) { - textureProperty.UpdateScale(material, scale); + textureProperty.UpdateScale(DecalMaterial, scale); } } - public void SetOpacity(Material material, float opacity) { - material.SetFloat(OpacityId, opacity); + public void SetOpacity(float opacity) { + DecalMaterial.SetFloat(OpacityId, opacity); + } + + public void SetCutoff(float cutoff) { + DecalMaterial.SetFloat(CutoffId, cutoff); } - public void SetCutoff(Material material, float cutoff) { - material.SetFloat(CutoffId, cutoff); + public void UpdateMaterials() { + UpdateMaterial(_decalMaterial); } - private Material MakeMaterial(Shader shader) { - var material = new Material(shader); - foreach (MaterialProperty property in _materialProperties) { + public void UpdateMaterial(Material material) { + foreach (var property in _materialProperties) { property.Modify(material); } - - return material; } } } \ No newline at end of file diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs index c14ab06..03dda7d 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs @@ -3,7 +3,7 @@ using UnityEngine; namespace ConformalDecals.MaterialModifiers { public class MaterialTextureProperty : MaterialProperty { - public Texture2D TextureRef { get; } + public Texture2D texture; public bool IsNormal { get; } public bool IsMain { get; } @@ -23,53 +23,53 @@ namespace ConformalDecals.MaterialModifiers { var textureUrl = node.GetValue("textureURL"); if ((textureUrl == null && IsNormal) || textureUrl == "Bump") { - TextureRef = Texture2D.normalTexture; + texture = Texture2D.normalTexture; } else if ((textureUrl == null && !IsNormal) || textureUrl == "White") { - TextureRef = Texture2D.whiteTexture; + texture = Texture2D.whiteTexture; } else if (textureUrl == "Black") { - TextureRef = Texture2D.blackTexture; + texture = Texture2D.blackTexture; } else { var textureInfo = GameDatabase.Instance.GetTextureInfo(textureUrl); if (textureInfo == null) throw new Exception($"Cannot find texture: '{textureUrl}'"); - TextureRef = IsNormal ? textureInfo.normalMap : textureInfo.texture; + texture = IsNormal ? textureInfo.normalMap : textureInfo.texture; } - if (TextureRef == null) throw new Exception($"Cannot get texture from texture info '{textureUrl}' isNormalMap = {IsNormal}"); + if (texture == null) throw new Exception($"Cannot get texture from texture info '{textureUrl}' isNormalMap = {IsNormal}"); - _tileRect = ParsePropertyRect(node, "tileRect", true, new Rect(0, 0, TextureRef.width, TextureRef.height)); + _tileRect = ParsePropertyRect(node, "tileRect", true, new Rect(0, 0, texture.width, texture.height)); - _textureScale.x = _tileRect.width / TextureRef.width; - _textureScale.y = _tileRect.height / TextureRef.height; + _textureScale.x = _tileRect.width / texture.width; + _textureScale.y = _tileRect.height / texture.height; - _textureOffset.x = _tileRect.x / TextureRef.width; - _textureOffset.y = _tileRect.y / TextureRef.height; + _textureOffset.x = _tileRect.x / texture.width; + _textureOffset.y = _tileRect.y / texture.height; } public MaterialTextureProperty(string name, Texture2D texture, Rect tileRect = default, bool isNormal = false, bool isMain = false, bool autoScale = false) : base(name) { - TextureRef = texture; + this.texture = texture; - _tileRect = tileRect == default ? new Rect(0, 0, TextureRef.width, TextureRef.height) : tileRect; + _tileRect = tileRect == default ? new Rect(0, 0, this.texture.width, this.texture.height) : tileRect; IsNormal = isNormal; IsMain = isMain; AutoScale = autoScale; - _textureScale.x = _tileRect.width / TextureRef.width; - _textureScale.y = _tileRect.height / TextureRef.height; + _textureScale.x = _tileRect.width / this.texture.width; + _textureScale.y = _tileRect.height / this.texture.height; - _textureOffset.x = _tileRect.x / TextureRef.width; - _textureOffset.y = _tileRect.y / TextureRef.height; + _textureOffset.x = _tileRect.x / this.texture.width; + _textureOffset.y = _tileRect.y / this.texture.height; } - + public override void Modify(Material material) { - material.SetTexture(_propertyID, TextureRef); + material.SetTexture(_propertyID, texture); material.SetTextureOffset(_propertyID, _textureOffset); material.SetTextureScale(_propertyID, _textureScale); } diff --git a/Source/ConformalDecals/ModuleConformalDecal.cs b/Source/ConformalDecals/ModuleConformalDecalBase.cs similarity index 83% rename from Source/ConformalDecals/ModuleConformalDecal.cs rename to Source/ConformalDecals/ModuleConformalDecalBase.cs index 8e2217b..63a4b23 100644 --- a/Source/ConformalDecals/ModuleConformalDecal.cs +++ b/Source/ConformalDecals/ModuleConformalDecalBase.cs @@ -5,7 +5,7 @@ using ConformalDecals.Util; using UnityEngine; namespace ConformalDecals { - public class ModuleConformalDecal : PartModule { + public abstract class ModuleConformalDecalBase : PartModule { [KSPField(guiName = "#LOC_ConformalDecals_gui-scale", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F2", guiUnits = "m"), UI_FloatRange(minValue = 0.05f, maxValue = 4f, stepIncrement = 0.05f)] public float scale = 1.0f; @@ -22,9 +22,6 @@ namespace ConformalDecals { UI_FloatRange(minValue = 0.0f, maxValue = 1f, stepIncrement = 0.05f)] public float cutoff = 0.5f; - [KSPField(guiName = "#LOC_ConformalDecals_gui-aspectratio", guiActive = false, guiActiveEditor = true, guiFormat = "F2")] - public float aspectRatio = 1.0f; - [KSPField] public string decalFront = string.Empty; [KSPField] public string decalBack = string.Empty; [KSPField] public string decalModel = string.Empty; @@ -52,7 +49,6 @@ namespace ConformalDecals { [KSPField] public MaterialPropertyCollection materialProperties; - [KSPField] public Material decalMaterial; [KSPField] public Material backMaterial; private static int _decalQueueCounter = -1; @@ -64,7 +60,7 @@ namespace ConformalDecals { private Bounds _decalBounds; private Vector2 _backTextureBaseScale; - public ModuleConformalDecal() { + public ModuleConformalDecalBase() { decalBackTransform = null; decalModelTransform = null; decalProjectorTransform = null; @@ -77,7 +73,6 @@ namespace ConformalDecals { _decalQueueCounter = (int) decalQueueRange.x; } - this.Log($"returning decal queue value {_decalQueueCounter}"); return _decalQueueCounter; } } @@ -88,7 +83,7 @@ namespace ConformalDecals { if (HighLogic.LoadedSceneIsEditor) { UpdateTweakables(); } - + if (materialProperties == null) { // materialProperties is null, so make a new one materialProperties = ScriptableObject.CreateInstance(); @@ -96,38 +91,27 @@ namespace ConformalDecals { } else { // materialProperties already exists, so make a copy - this.Log($"{materialProperties == null}"); 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.MainMaterialTextureProperty; - if (mainTexture != null) { - aspectRatio = mainTexture.AspectRatio; - } - else { - aspectRatio = 1; + // 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 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); + } } // find front transform @@ -182,35 +166,17 @@ namespace ConformalDecals { if (HighLogic.LoadedSceneIsEditor) { GameEvents.onEditorPartEvent.Add(OnEditorEvent); GameEvents.onVariantApplied.Add(OnVariantApplied); - + UpdateTweakables(); } - + // generate orthogonal projection matrix and offset it by 0.5 on x and y axes _orthoMatrix = Matrix4x4.identity; _orthoMatrix[0, 3] = 0.5f; _orthoMatrix[1, 3] = 0.5f; // instantiate decal material and set uniqueish queue - decalMaterial = Material.Instantiate(decalMaterial); - decalMaterial.renderQueue = DecalQueue; - - // 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 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); - } - } + materialProperties.SetRenderQueue(DecalQueue); // set initial attachment state if (part.parent == null) { @@ -237,8 +203,8 @@ namespace ConformalDecals { } private void OnMaterialTweakEvent(BaseField field, object obj) { - materialProperties.SetOpacity(decalMaterial, opacity); - materialProperties.SetCutoff(decalMaterial, cutoff); + materialProperties.SetOpacity(opacity); + materialProperties.SetCutoff(cutoff); } private void OnVariantApplied(Part eventPart, PartVariant variant) { @@ -301,7 +267,7 @@ namespace ConformalDecals { } private void UpdateScale() { - var size = new Vector2(scale, scale * aspectRatio); + var size = new Vector2(scale, scale * materialProperties.AspectRatio); // update orthogonal matrix scale _orthoMatrix[0, 0] = 1 / size.x; @@ -321,7 +287,7 @@ namespace ConformalDecals { } // update material scale - materialProperties.SetScale(decalMaterial, size); + materialProperties.SetScale(size); } private void UpdateProjection() { @@ -418,13 +384,13 @@ namespace ConformalDecals { cutoffField.uiControlEditor.onFieldChanged = OnMaterialTweakEvent; } } - + private void Render(Camera camera) { if (!_isAttached) return; // render on each target object foreach (var target in _targets) { - target.Render(decalMaterial, part.mpb, camera); + target.Render(materialProperties.DecalMaterial, part.mpb, camera); } } } diff --git a/Source/ConformalDecals/ModuleConformalDecalFlag.cs b/Source/ConformalDecals/ModuleConformalDecalFlag.cs new file mode 100644 index 0000000..f00bc62 --- /dev/null +++ b/Source/ConformalDecals/ModuleConformalDecalFlag.cs @@ -0,0 +1,32 @@ +using System; +using ConformalDecals.MaterialModifiers; +using ConformalDecals.Util; +using UnityEngine; + +namespace ConformalDecals { + public class ModuleConformalDecalFlag : ModuleConformalDecalBase { + + [KSPField] + private MaterialTextureProperty _flagTextureProperty; + + public override void OnLoad(ConfigNode node) { + base.OnLoad(node); + + _flagTextureProperty = new MaterialTextureProperty("_MainTex", Texture2D.whiteTexture); + materialProperties.AddProperty(_flagTextureProperty); + } + + public override void OnStart(StartState state) { + base.OnStart(state); + + UpdateFlag(EditorLogic.FlagURL != string.Empty ? EditorLogic.FlagURL : HighLogic.CurrentGame.flagURL); + GameEvents.onMissionFlagSelect.Add(UpdateFlag); + } + + private void UpdateFlag(string flagUrl) { + _flagTextureProperty.texture = GameDatabase.Instance.GetTexture(flagUrl, false); + + materialProperties.UpdateMaterials(); + } + } +} \ No newline at end of file diff --git a/Source/ConformalDecals/ModuleConformalDecalGeneric.cs b/Source/ConformalDecals/ModuleConformalDecalGeneric.cs new file mode 100644 index 0000000..4b523fa --- /dev/null +++ b/Source/ConformalDecals/ModuleConformalDecalGeneric.cs @@ -0,0 +1,26 @@ +using ConformalDecals.MaterialModifiers; +using ConformalDecals.Util; +using UnityEngine; + +namespace ConformalDecals { + public class ModuleConformalDecalGeneric : ModuleConformalDecalBase { + public override void OnLoad(ConfigNode node) { + base.OnLoad(node); + + // 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)); + } + } + } +} \ No newline at end of file