diff --git a/GameData/ConformalDecals/Assets/decal-blank.mu b/GameData/ConformalDecals/Assets/decal-blank.mu
index d06471c..d35fc4c 100644
--- a/GameData/ConformalDecals/Assets/decal-blank.mu
+++ b/GameData/ConformalDecals/Assets/decal-blank.mu
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4ec3546be98b11c336ddaffb9c4e68c5a27a364922b5fc1f7ac7d909d9272938
-size 4892
+oid sha256:cf169fd66e880660d0f1d1cd7dd255ca6127f8ffaec5228ed00fcb16d7093fbc
+size 4972
diff --git a/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/GameData/ConformalDecals/Plugins/ConformalDecals.dll
index 1f5442e..d0f41f3 100644
--- a/GameData/ConformalDecals/Plugins/ConformalDecals.dll
+++ b/GameData/ConformalDecals/Plugins/ConformalDecals.dll
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f667cfa986e09296c083693e3a95294b277abe19099a760881d2bd85fecc7c9c
-size 37888
+oid sha256:9b4405eaa6d2efc358a8364e1cc9ea3eba73c42f6a3d50da1433447ca989068a
+size 41984
diff --git a/GameData/ConformalDecals/Resources/ConformalDecals.cfg b/GameData/ConformalDecals/Resources/ConformalDecals.cfg
index 72c6e59..bed0e9c 100644
--- a/GameData/ConformalDecals/Resources/ConformalDecals.cfg
+++ b/GameData/ConformalDecals/Resources/ConformalDecals.cfg
@@ -1,4 +1,7 @@
CONFORMALDECALS {
+ decalLayer = 31
+ selectableInFlight = false
+
SHADERBLACKLIST {
shader = DepthMask
shader = KSP/Alpha/Cutoff
diff --git a/Source/ConformalDecals/ConformalDecals.csproj b/Source/ConformalDecals/ConformalDecals.csproj
index 19d5ed4..fd1d246 100644
--- a/Source/ConformalDecals/ConformalDecals.csproj
+++ b/Source/ConformalDecals/ConformalDecals.csproj
@@ -54,17 +54,19 @@
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/Source/ConformalDecals/DecalConfig.cs b/Source/ConformalDecals/DecalConfig.cs
index e446b94..ca35d8d 100644
--- a/Source/ConformalDecals/DecalConfig.cs
+++ b/Source/ConformalDecals/DecalConfig.cs
@@ -1,14 +1,20 @@
using System.Collections.Generic;
+using ConformalDecals.Util;
using UnityEngine;
-using UnityEngine.Experimental.Rendering;
namespace ConformalDecals {
public static class DecalConfig {
- private static Texture2D _blankNormal;
+ private static Texture2D _blankNormal;
private static List _shaderBlacklist;
-
+ private static int _decalLayer = 31;
+ private static bool _selectableInFlight = false;
+
public static Texture2D BlankNormal => _blankNormal;
+ public static int DecalLayer => _decalLayer;
+
+ public static bool SelectableInFlight => _selectableInFlight;
+
public static bool IsBlacklisted(Shader shader) {
return IsBlacklisted(shader.name);
}
@@ -22,6 +28,9 @@ namespace ConformalDecals {
foreach (var shaderName in blacklist.GetValuesList("shader")) {
_shaderBlacklist.Add(shaderName);
}
+
+ ParseUtil.ParseIntIndirect(ref _decalLayer, node, "decalLayer");
+ ParseUtil.ParseBoolIndirect(ref _selectableInFlight, node, "selectableInFlight");
}
}
@@ -30,7 +39,7 @@ namespace ConformalDecals {
var width = 2;
var height = 2;
var color = new Color32(255, 128, 128, 128);
- var colors = new Color32[] { color, color, color, color };
+ var colors = new[] {color, color, color, color};
var tex = new Texture2D(width, height, TextureFormat.RGBA32, false);
for (var x = 0; x <= width; x++) {
@@ -38,11 +47,13 @@ namespace ConformalDecals {
tex.SetPixels32(colors);
}
}
+
tex.Apply();
return tex;
}
+ // ReSharper disable once UnusedMember.Global
public static void ModuleManagerPostLoad() {
_shaderBlacklist = new List();
@@ -55,6 +66,14 @@ namespace ConformalDecals {
}
}
+ // setup physics for decals, ignore collision with everything
+ Physics.IgnoreLayerCollision(_decalLayer, 1, true); // default
+ Physics.IgnoreLayerCollision(_decalLayer, 17, true); // EVA
+ Physics.IgnoreLayerCollision(_decalLayer, 19, true); // PhysicalObjects
+ Physics.IgnoreLayerCollision(_decalLayer, 23, true); // AeroFXIgnore
+ Physics.IgnoreLayerCollision(_decalLayer, 26, true); // wheelCollidersIgnore
+ Physics.IgnoreLayerCollision(_decalLayer, 27, true); // wheelColliders
+
_blankNormal = MakeBlankNormal();
}
}
diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs
deleted file mode 100644
index 0bce511..0000000
--- a/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using System;
-using UnityEngine;
-
-namespace ConformalDecals.MaterialModifiers {
- public abstract class MaterialProperty : ScriptableObject {
- public string PropertyName {
- get => _propertyName;
- set {
- _propertyName = value;
- _propertyID = Shader.PropertyToID(_propertyName);
- }
- }
-
- [SerializeField] protected int _propertyID;
- [SerializeField] protected string _propertyName;
-
- public virtual void ParseNode(ConfigNode node) {
- if (node == null) throw new ArgumentNullException(nameof(node));
-
- PropertyName = node.GetValue("name");
- Debug.Log($"Parsing material property {_propertyName}");
- }
-
- public abstract void Modify(Material material);
-
- private delegate bool TryParseDelegate(string valueString, out T value);
-
- protected bool ParsePropertyBool(ConfigNode node, string valueName, bool isOptional = false, bool defaultValue = false) {
- return ParsePropertyValue(node, valueName, bool.TryParse, isOptional, defaultValue);
- }
-
- protected float ParsePropertyFloat(ConfigNode node, string valueName, bool isOptional = false, float defaultValue = 0.0f) {
- return ParsePropertyValue(node, valueName, float.TryParse, isOptional, defaultValue);
- }
-
- protected int ParsePropertyInt(ConfigNode node, string valueName, bool isOptional = false, int defaultValue = 0) {
- return ParsePropertyValue(node, valueName, int.TryParse, isOptional, defaultValue);
- }
-
- protected Color ParsePropertyColor(ConfigNode node, string valueName, bool isOptional = false, Color defaultValue = default) {
- return ParsePropertyValue(node, valueName, ParseExtensions.TryParseColor, isOptional, defaultValue);
- }
-
- protected Rect ParsePropertyRect(ConfigNode node, string valueName, bool isOptional = false, Rect defaultValue = default) {
- return ParsePropertyValue(node, valueName, ParseExtensions.TryParseRect, isOptional, defaultValue);
- }
-
- protected Vector2 ParsePropertyVector2(ConfigNode node, string valueName, bool isOptional = false, Vector2 defaultValue = default) {
- return ParsePropertyValue(node, valueName, ParseExtensions.TryParseVector2, isOptional, defaultValue);
- }
-
- private T ParsePropertyValue(ConfigNode node, string valueName, TryParseDelegate tryParse, bool isOptional = false, T defaultValue = default) {
- string valueString = node.GetValue(valueName);
-
- if (isOptional) {
- if (string.IsNullOrEmpty(valueString)) return defaultValue;
- }
- else {
- if (valueString == null)
- 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 '{PropertyName}'");
- }
-
- if (tryParse(valueString, out var value)) {
- return value;
- }
-
- if (isOptional) {
- return defaultValue;
- }
-
- else {
- throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} in property '{PropertyName}' : '{valueString}");
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs b/Source/ConformalDecals/MaterialProperties/MaterialColorProperty.cs
similarity index 60%
rename from Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs
rename to Source/ConformalDecals/MaterialProperties/MaterialColorProperty.cs
index f60d388..5b770e6 100644
--- a/Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialColorProperty.cs
@@ -1,18 +1,19 @@
using System;
+using ConformalDecals.Util;
using UnityEngine;
-namespace ConformalDecals.MaterialModifiers {
+namespace ConformalDecals.MaterialProperties {
public class MaterialColorProperty : MaterialProperty {
- [SerializeField] public Color color;
+ [SerializeField] public Color32 color = new Color32(0, 0, 0, byte.MaxValue);
public override void ParseNode(ConfigNode node) {
base.ParseNode(node);
- color = ParsePropertyColor(node, "color", true, color);
+ ParseUtil.ParseColor32Indirect(ref color, node, "color");
}
public override void Modify(Material material) {
- if (material == null) throw new ArgumentNullException("material cannot be null");
+ if (material == null) throw new ArgumentNullException(nameof(material));
material.SetColor(_propertyID, color);
}
diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs b/Source/ConformalDecals/MaterialProperties/MaterialFloatProperty.cs
similarity index 72%
rename from Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs
rename to Source/ConformalDecals/MaterialProperties/MaterialFloatProperty.cs
index 1dad8df..6745dfc 100644
--- a/Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialFloatProperty.cs
@@ -1,18 +1,19 @@
using System;
+using ConformalDecals.Util;
using UnityEngine;
-namespace ConformalDecals.MaterialModifiers {
+namespace ConformalDecals.MaterialProperties {
public class MaterialFloatProperty : MaterialProperty {
[SerializeField] public float value;
public override void ParseNode(ConfigNode node) {
base.ParseNode(node);
- value = ParsePropertyFloat(node, "value", true, value);
+ ParseUtil.ParseFloatIndirect(ref value, node, "value");
}
public override void Modify(Material material) {
- if (material == null) throw new ArgumentNullException("material cannot be null");
+ if (material == null) throw new ArgumentNullException(nameof(material));
material.SetFloat(_propertyID, value);
}
diff --git a/Source/ConformalDecals/MaterialProperties/MaterialProperty.cs b/Source/ConformalDecals/MaterialProperties/MaterialProperty.cs
new file mode 100644
index 0000000..6d792ab
--- /dev/null
+++ b/Source/ConformalDecals/MaterialProperties/MaterialProperty.cs
@@ -0,0 +1,26 @@
+using System;
+using UnityEngine;
+
+namespace ConformalDecals.MaterialProperties {
+ public abstract class MaterialProperty : ScriptableObject {
+ public string PropertyName {
+ get => _propertyName;
+ set {
+ _propertyName = value;
+ _propertyID = Shader.PropertyToID(_propertyName);
+ }
+ }
+
+ [SerializeField] protected int _propertyID;
+ [SerializeField] protected string _propertyName;
+
+ public virtual void ParseNode(ConfigNode node) {
+ if (node == null) throw new ArgumentNullException(nameof(node));
+
+ PropertyName = node.GetValue("name");
+ Debug.Log($"Parsing material property {_propertyName}");
+ }
+
+ public abstract void Modify(Material material);
+ }
+}
\ No newline at end of file
diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs b/Source/ConformalDecals/MaterialProperties/MaterialPropertyCollection.cs
similarity index 97%
rename from Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs
rename to Source/ConformalDecals/MaterialProperties/MaterialPropertyCollection.cs
index 8698f88..b61912b 100644
--- a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialPropertyCollection.cs
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
+using ConformalDecals.Util;
using UniLinq;
using UnityEngine;
using UnityEngine.Rendering;
-namespace ConformalDecals.MaterialModifiers {
+namespace ConformalDecals.MaterialProperties {
public class MaterialPropertyCollection : ScriptableObject, ISerializationCallbackReceiver {
public int RenderQueue {
get => _renderQueue;
@@ -163,8 +164,8 @@ namespace ConformalDecals.MaterialModifiers {
}
public T ParseProperty(ConfigNode node) where T : MaterialProperty {
- var propertyName = node.GetValue("name");
- if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("node has no name");
+ string propertyName = "";
+ if (!ParseUtil.ParseStringIndirect(ref propertyName, node, "name")) throw new ArgumentException("node has no name");
var newProperty = AddOrGetProperty(propertyName);
newProperty.ParseNode(node);
diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs b/Source/ConformalDecals/MaterialProperties/MaterialTextureProperty.cs
similarity index 79%
rename from Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs
rename to Source/ConformalDecals/MaterialProperties/MaterialTextureProperty.cs
index e6004bb..fbbeacf 100644
--- a/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialTextureProperty.cs
@@ -1,7 +1,8 @@
using System;
+using ConformalDecals.Util;
using UnityEngine;
-namespace ConformalDecals.MaterialModifiers {
+namespace ConformalDecals.MaterialProperties {
public class MaterialTextureProperty : MaterialProperty {
[SerializeField] public bool isNormal;
[SerializeField] public bool isMain;
@@ -9,7 +10,7 @@ namespace ConformalDecals.MaterialModifiers {
[SerializeField] public bool autoTile;
[SerializeField] private string _textureUrl;
- [SerializeField] private Texture2D _texture;
+ [SerializeField] private Texture2D _texture = Texture2D.whiteTexture;
[SerializeField] private bool _hasTile;
[SerializeField] private Rect _tileRect;
@@ -42,24 +43,17 @@ namespace ConformalDecals.MaterialModifiers {
public override void ParseNode(ConfigNode node) {
base.ParseNode(node);
- isNormal = ParsePropertyBool(node, "isNormalMap", true, (PropertyName == "_BumpMap") || (PropertyName == "_DecalBumpMap") || isNormal);
- isMain = ParsePropertyBool(node, "isMain", true, isMain);
- autoScale = ParsePropertyBool(node, "autoScale", true, autoScale);
- autoTile = ParsePropertyBool(node, "autoTile", true, autoTile);
+ ParseUtil.ParseBoolIndirect(ref isMain, node, "isMain");
+ ParseUtil.ParseBoolIndirect(ref isNormal, node, "isNormalMap");
+ ParseUtil.ParseBoolIndirect(ref autoScale, node, "autoScale");
+ ParseUtil.ParseBoolIndirect(ref autoTile, node, "autoTile");
- var textureUrl = node.GetValue("textureUrl");
-
- if (string.IsNullOrEmpty(textureUrl)) {
- if (string.IsNullOrEmpty(_textureUrl)) {
- TextureUrl = "";
- }
- }
- else {
- TextureUrl = node.GetValue("textureUrl");
+ if (!autoTile) {
+ ParseUtil.ParseRectIndirect(ref _tileRect, node, "tile");
}
- if (node.HasValue("tile") && !autoTile) {
- SetTile(ParsePropertyRect(node, "tile", true, _tileRect));
+ if (ParseUtil.ParseStringIndirect(ref _textureUrl, node, "textureUrl")) {
+ _texture = LoadTexture(_textureUrl, isNormal);
}
}
diff --git a/Source/ConformalDecals/ModuleConformalDecal.cs b/Source/ConformalDecals/ModuleConformalDecal.cs
index 863eea7..c5b06e2 100644
--- a/Source/ConformalDecals/ModuleConformalDecal.cs
+++ b/Source/ConformalDecals/ModuleConformalDecal.cs
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
-using ConformalDecals.MaterialModifiers;
+using System.Diagnostics.CodeAnalysis;
+using ConformalDecals.MaterialProperties;
using ConformalDecals.Util;
using UnityEngine;
namespace ConformalDecals {
public class ModuleConformalDecal : PartModule {
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
public enum DecalScaleMode {
HEIGHT,
WIDTH,
@@ -17,43 +19,21 @@ namespace ConformalDecals {
// CONFIGURABLE VALUES
- ///
- /// Shader name. Should be one that supports decal projection.
- ///
[KSPField] public string shader = "ConformalDecals/Paint/Diffuse";
- ///
- /// Decal front transform name. Required
- ///
- [KSPField] public string decalFront = "Decal-Front";
-
- ///
- /// Decal back transform name. Required if is true.
- ///
- [KSPField] public string decalBack = "Decal-Back";
-
- ///
- /// Decal model transform name. Is rescaled to preview the decal scale when unattached.
- ///
- ///
- /// If unspecified, the decal front transform is used instead.
- ///
- [KSPField] public string decalModel = "Decal-Model";
-
- ///
- /// Decal projector transform name. The decal will project along the +Z axis of this transform.
- ///
- ///
- /// if unspecified, the part "model" transform will be used instead.
- ///
+ [KSPField] public string decalFront = "Decal-Front";
+ [KSPField] public string decalBack = "Decal-Back";
+ [KSPField] public string decalModel = "Decal-Model";
[KSPField] public string decalProjector = "Decal-Projector";
+ [KSPField] public string decalCollider = "Decal-Collider";
// Parameters
- [KSPField] public bool scaleAdjustable = true;
- [KSPField] public float defaultScale = 1;
- [KSPField] public Vector2 scaleRange = new Vector2(0, 4);
- [KSPField] public DecalScaleMode scaleMode = DecalScaleMode.HEIGHT;
+ [KSPField] public bool scaleAdjustable = true;
+ [KSPField] public float defaultScale = 1;
+ [KSPField] public Vector2 scaleRange = new Vector2(0, 4);
+
+ [KSPField] public DecalScaleMode scaleMode = DecalScaleMode.HEIGHT;
[KSPField] public bool depthAdjustable = true;
[KSPField] public float defaultDepth = 0.1f;
@@ -75,56 +55,40 @@ namespace ConformalDecals {
[KSPField] public Vector2 tileSize;
[KSPField] public int tileIndex = -1;
- ///
- /// Should the back material scale be updated automatically?
- ///
[KSPField] public bool updateBackScale = true;
-
+ [KSPField] public bool selectableInFlight;
// INTERNAL VALUES
-
- ///
- /// Decal scale factor, in meters.
- ///
[KSPField(guiName = "#LOC_ConformalDecals_gui-scale", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F2", guiUnits = "m"),
UI_FloatRange(stepIncrement = 0.05f)]
public float scale = 1.0f;
- ///
- /// Projection depth value for the decal projector, in meters.
- ///
[KSPField(guiName = "#LOC_ConformalDecals_gui-depth", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F2", guiUnits = "m"),
UI_FloatRange(stepIncrement = 0.02f)]
public float depth = 0.2f;
- ///
- /// Opacity value for the decal shader.
- ///
[KSPField(guiName = "#LOC_ConformalDecals_gui-opacity", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "P0"),
UI_FloatRange(stepIncrement = 0.05f)]
public float opacity = 1.0f;
- ///
- /// Alpha cutoff value for the decal shader.
- ///
[KSPField(guiName = "#LOC_ConformalDecals_gui-cutoff", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "P0"),
UI_FloatRange(stepIncrement = 0.05f)]
public float cutoff = 0.5f;
- ///
- /// Edge wear value for the decal shader. Only relevent when useBaseNormal is true and the shader is a paint shader
- ///
[KSPField(guiName = "#LOC_ConformalDecals_gui-wear", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F0"),
UI_FloatRange()]
public float wear = 100;
+ [KSPField(isPersistant = true)] public bool projectMultiple; // reserved for future features. do not modify
+
[KSPField] public MaterialPropertyCollection materialProperties;
[KSPField] public Transform decalFrontTransform;
[KSPField] public Transform decalBackTransform;
[KSPField] public Transform decalModelTransform;
[KSPField] public Transform decalProjectorTransform;
-
+ [KSPField] public Transform decalColliderTransform;
+
[KSPField] public Material backMaterial;
[KSPField] public Vector2 backTextureBaseScale;
@@ -137,10 +101,10 @@ namespace ConformalDecals {
private bool _isAttached;
private Matrix4x4 _orthoMatrix;
- private Material _decalMaterial;
- private Material _previewMaterial;
- private BoxCollider _boundsCollider;
-
+ private Material _decalMaterial;
+ private Material _previewMaterial;
+ private MeshRenderer _boundsRenderer;
+
private int DecalQueue {
get {
_decalQueueCounter++;
@@ -169,43 +133,22 @@ namespace ConformalDecals {
this.Log("Loading module");
try {
// SETUP TRANSFORMS
-
- // find front transform
decalFrontTransform = part.FindModelTransform(decalFront);
if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'.");
- // find back transform
- if (string.IsNullOrEmpty(decalBack)) {
- if (updateBackScale) {
- this.LogWarning("updateBackScale is true but has no specified decalBack transform!");
- this.LogWarning("Setting updateBackScale to false.");
- updateBackScale = false;
- }
- }
- else {
- decalBackTransform = part.FindModelTransform(decalBack);
- if (decalBackTransform == null) throw new FormatException($"Could not find decalBack transform: '{decalBack}'.");
- }
+ decalBackTransform = part.FindModelTransform(decalBack);
+ if (decalBackTransform == null) throw new FormatException($"Could not find decalBack transform: '{decalBack}'.");
- // find model transform
- if (string.IsNullOrEmpty(decalModel)) {
- decalModelTransform = decalFrontTransform;
- }
- else {
- decalModelTransform = part.FindModelTransform(decalModel);
- if (decalModelTransform == null) throw new FormatException($"Could not find decalModel transform: '{decalModel}'.");
- }
+ decalModelTransform = part.FindModelTransform(decalModel);
+ if (decalModelTransform == null) throw new FormatException($"Could not find decalModel transform: '{decalModel}'.");
- // find projector transform
- if (string.IsNullOrEmpty(decalProjector)) {
- decalProjectorTransform = part.transform;
- }
- else {
- decalProjectorTransform = part.FindModelTransform(decalProjector);
- if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'.");
- }
+ decalProjectorTransform = part.FindModelTransform(decalProjector);
+ if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'.");
- // get back material if necessary
+ decalColliderTransform = part.FindModelTransform(decalCollider);
+ if (decalColliderTransform == null) throw new FormatException($"Could not find decalCollider transform: '{decalCollider}'.");
+
+ // SETUP BACK MATERIAL
if (updateBackScale) {
this.Log("Getting material and base scale for back material");
var backRenderer = decalBackTransform.GetComponent();
@@ -294,17 +237,11 @@ namespace ConformalDecals {
public override void OnStart(StartState state) {
this.Log("Starting module");
- // handle tweakables
- if (HighLogic.LoadedSceneIsEditor) {
- GameEvents.onEditorPartEvent.Add(OnEditorEvent);
- GameEvents.onVariantApplied.Add(OnVariantApplied);
- UpdateTweakables();
- }
materialProperties.RenderQueue = DecalQueue;
- _boundsCollider = decalProjectorTransform.GetComponent();
+ _boundsRenderer = decalProjectorTransform.GetComponent();
UpdateMaterials();
@@ -317,12 +254,40 @@ namespace ConformalDecals {
OnAttach();
}
}
+
+ // handle tweakables
+ if (HighLogic.LoadedSceneIsEditor) {
+ GameEvents.onEditorPartEvent.Add(OnEditorEvent);
+ GameEvents.onVariantApplied.Add(OnVariantApplied);
+
+ UpdateTweakables();
+ }
+
+ // handle flight events
+ if (HighLogic.LoadedSceneIsFlight) {
+ GameEvents.onPartWillDie.Add(OnPartWillDie);
+
+ Part.layerMask |= 1 << DecalConfig.DecalLayer;
+ decalColliderTransform.gameObject.layer = DecalConfig.DecalLayer;
+
+ if (!selectableInFlight || !DecalConfig.SelectableInFlight) {
+ decalColliderTransform.GetComponent().enabled = false;
+ }
+
+ if (part.parent == null) part.explode();
+ }
}
public virtual void OnDestroy() {
// remove GameEvents
- GameEvents.onEditorPartEvent.Remove(OnEditorEvent);
- GameEvents.onVariantApplied.Remove(OnVariantApplied);
+ if (HighLogic.LoadedSceneIsEditor) {
+ GameEvents.onEditorPartEvent.Remove(OnEditorEvent);
+ GameEvents.onVariantApplied.Remove(OnVariantApplied);
+ }
+
+ if (HighLogic.LoadedSceneIsFlight) {
+ GameEvents.onPartWillDie.Remove(OnPartWillDie);
+ }
// remove from preCull delegate
Camera.onPreCull -= Render;
@@ -381,6 +346,13 @@ namespace ConformalDecals {
}
}
+ protected void OnPartWillDie(Part willDie) {
+ if (willDie == part.parent) {
+ this.Log("Parent part about to be destroyed! Killing decal part.");
+ part.Die();
+ }
+ }
+
protected void OnAttach() {
if (part.parent == null) {
this.LogError("Attach function called but part has no parent!");
@@ -423,6 +395,8 @@ namespace ConformalDecals {
}
protected void UpdateScale() {
+ scale = Mathf.Max(0.01f, scale);
+ depth = Mathf.Max(0.01f, depth);
var aspectRatio = materialProperties.AspectRatio;
Vector2 size;
@@ -463,7 +437,7 @@ namespace ConformalDecals {
// update projection
foreach (var target in _targets) {
- target.Project(_orthoMatrix, decalProjectorTransform, _boundsCollider.bounds, useBaseNormal);
+ target.Project(_orthoMatrix, decalProjectorTransform, _boundsRenderer.bounds, useBaseNormal);
}
}
else {
@@ -597,7 +571,7 @@ namespace ConformalDecals {
public void Render(Camera camera) {
if (!_isAttached) return;
-
+
// render on each target object
foreach (var target in _targets) {
target.Render(_decalMaterial, part.mpb, camera);
diff --git a/Source/ConformalDecals/Test/TestLayers.cs b/Source/ConformalDecals/Test/TestLayers.cs
new file mode 100644
index 0000000..50e413a
--- /dev/null
+++ b/Source/ConformalDecals/Test/TestLayers.cs
@@ -0,0 +1,34 @@
+using System;
+using UnityEngine;
+
+namespace ConformalDecals.Test {
+ public class TestLayers : PartModule {
+
+ [KSPField(guiActive = true)]
+ public int layer = 2;
+
+ public override void OnStart(StartState state) {
+ base.OnStart(state);
+
+
+ Part.layerMask.value |= (1 << 3);
+ }
+
+ public void Update() {
+ foreach (var collider in GameObject.FindObjectsOfType()) {
+ if (collider.gameObject.layer == 3) {
+ Debug.Log($"Has layer 3: {collider.gameObject.name}");
+ }
+ }
+ }
+
+ [KSPEvent(guiActive = true, guiActiveEditor = true, guiName = "switch layers")]
+ public void Switch() {
+ Debug.Log(Part.layerMask.value);
+
+ var cube = part.FindModelTransform("test");
+ layer = (layer + 1) % 32;
+ cube.gameObject.layer = layer;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ConformalDecals/Util/ParseUtil.cs b/Source/ConformalDecals/Util/ParseUtil.cs
new file mode 100644
index 0000000..fcb1ecd
--- /dev/null
+++ b/Source/ConformalDecals/Util/ParseUtil.cs
@@ -0,0 +1,216 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using UnityEngine;
+
+namespace ConformalDecals.Util {
+ public static class ParseUtil {
+ private static readonly Dictionary NamedColors = new Dictionary();
+ private static readonly char[] Separator = {',', ' ', '\t'};
+
+ public delegate bool TryParseDelegate(string valueString, out T value);
+
+ static ParseUtil() {
+ // setup named colors
+ foreach (var propertyInfo in typeof(Color).GetProperties(BindingFlags.Static | BindingFlags.Public)) {
+ if (!propertyInfo.CanRead) continue;
+ if (propertyInfo.PropertyType != typeof(Color)) continue;
+
+ NamedColors.Add(propertyInfo.Name, (Color) propertyInfo.GetValue(null, null));
+ }
+
+ foreach (var propertyInfo in typeof(XKCDColors).GetProperties(BindingFlags.Static | BindingFlags.Public)) {
+ if (!propertyInfo.CanRead) continue;
+ if (propertyInfo.PropertyType != typeof(Color)) continue;
+
+ if (NamedColors.ContainsKey(propertyInfo.Name)) throw new Exception("duplicate key " + propertyInfo.Name);
+
+ NamedColors.Add(propertyInfo.Name, (Color) propertyInfo.GetValue(null, null));
+ }
+ }
+
+ public static string ParseString(ConfigNode node, string valueName, bool isOptional = false, string defaultValue = "") {
+ if (!node.HasValue(valueName)) throw new FormatException($"Missing value for {valueName}");
+
+ return node.GetValue(valueName);
+ }
+
+ public static bool ParseStringIndirect(ref string value, ConfigNode node, string valueName) {
+ if (node.HasValue(valueName)) {
+ value = node.GetValue(valueName);
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool ParseBool(ConfigNode node, string valueName, bool isOptional = false, bool defaultValue = false) {
+ return ParseValue(node, valueName, bool.TryParse, isOptional, defaultValue);
+ }
+
+ public static bool ParseBoolIndirect(ref bool value, ConfigNode node, string valueName) {
+ return ParseValueIndirect(ref value, node, valueName, bool.TryParse);
+ }
+
+ public static float ParseFloat(ConfigNode node, string valueName, bool isOptional = false, float defaultValue = 0.0f) {
+ return ParseValue(node, valueName, float.TryParse, isOptional, defaultValue);
+ }
+
+ public static bool ParseFloatIndirect(ref float value, ConfigNode node, string valueName) {
+ return ParseValueIndirect(ref value, node, valueName, float.TryParse);
+ }
+
+ public static int ParseInt(ConfigNode node, string valueName, bool isOptional = false, int defaultValue = 0) {
+ return ParseValue(node, valueName, int.TryParse, isOptional, defaultValue);
+ }
+
+ public static bool ParseIntIndirect(ref int value, ConfigNode node, string valueName) {
+ return ParseValueIndirect(ref value, node, valueName, int.TryParse);
+ }
+
+ public static Color32 ParseColor32(ConfigNode node, string valueName, bool isOptional = false, Color32 defaultValue = default) {
+ return ParseValue(node, valueName, TryParseColor32, isOptional, defaultValue);
+ }
+
+ public static bool ParseColor32Indirect(ref Color32 value, ConfigNode node, string valueName) {
+ return ParseValueIndirect(ref value, node, valueName, TryParseColor32);
+ }
+
+ public static Rect ParseRect(ConfigNode node, string valueName, bool isOptional = false, Rect defaultValue = default) {
+ return ParseValue(node, valueName, ParseExtensions.TryParseRect, isOptional, defaultValue);
+ }
+
+ public static bool ParseRectIndirect(ref Rect value, ConfigNode node, string valueName) {
+ return ParseValueIndirect(ref value, node, valueName, ParseExtensions.TryParseRect);
+ }
+
+ public static Vector2 ParseVector2(ConfigNode node, string valueName, bool isOptional = false, Vector2 defaultValue = default) {
+ return ParseValue(node, valueName, ParseExtensions.TryParseVector2, isOptional, defaultValue);
+ }
+
+ public static bool ParseVector2Indirect(ref Vector2 value, ConfigNode node, string valueName) {
+ return ParseValueIndirect(ref value, node, valueName, ParseExtensions.TryParseVector2);
+ }
+
+ public static Vector3 ParseVector3(ConfigNode node, string valueName, bool isOptional = false, Vector3 defaultValue = default) {
+ return ParseValue(node, valueName, ParseExtensions.TryParseVector3, isOptional, defaultValue);
+ }
+
+ public static bool ParseVector3Indirect(ref Vector3 value, ConfigNode node, string valueName) {
+ return ParseValueIndirect(ref value, node, valueName, ParseExtensions.TryParseVector3);
+ }
+
+ public static T ParseValue(ConfigNode node, string valueName, TryParseDelegate tryParse, bool isOptional = false, T defaultValue = default) {
+ string valueString = node.GetValue(valueName);
+
+ if (isOptional) {
+ if (string.IsNullOrEmpty(valueString)) return defaultValue;
+ }
+ else {
+ if (valueString == null)
+ throw new FormatException($"Missing {typeof(T)} value for {valueName}");
+
+ if (valueString == string.Empty)
+ throw new FormatException($"Empty {typeof(T)} value for {valueName}");
+ }
+
+ if (tryParse(valueString, out var value)) {
+ return value;
+ }
+
+ if (isOptional) {
+ return defaultValue;
+ }
+
+ else {
+ throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} : '{valueString}");
+ }
+ }
+
+ public static bool ParseValueIndirect(ref T value, ConfigNode node, string valueName, TryParseDelegate tryParse) {
+ if (!node.HasValue(valueName)) return false;
+
+ var valueString = node.GetValue(valueName);
+ if (tryParse(valueString, out var newValue)) {
+ value = newValue;
+ return true;
+ }
+
+ throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} : '{valueString}");
+ }
+
+ public static bool TryParseColor32(string valueString, out Color32 value) {
+ value = new Color32(0, 0, 0, byte.MaxValue);
+
+ // HTML-style hex color
+ if (valueString[0] == '#') {
+ var hexColorString = valueString.Substring(1);
+
+ if (!int.TryParse(hexColorString, System.Globalization.NumberStyles.HexNumber, null, out var hexColor)) return false;
+
+ switch (hexColorString.Length) {
+ case 8: // RRGGBBAA
+ value.a = (byte) (hexColor & 0xFF);
+ hexColor >>= 8;
+ goto case 6;
+
+ case 6: // RRGGBB
+ value.b = (byte) (hexColor & 0xFF);
+ hexColor >>= 8;
+ value.g = (byte) (hexColor & 0xFF);
+ hexColor >>= 8;
+ value.r = (byte) (hexColor & 0xFF);
+ return true;
+
+ case 4: // RGBA
+ value.a = (byte) ((hexColor & 0xF) << 4);
+ hexColor >>= 4;
+ goto case 3;
+
+ case 3: // RGB
+ value.b = (byte) (hexColor & 0xF << 4);
+ hexColor >>= 4;
+ value.g = (byte) (hexColor & 0xF << 4);
+ hexColor >>= 4;
+ value.r = (byte) (hexColor & 0xF << 4);
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ // named color
+ if (NamedColors.TryGetValue(valueString, out var namedColor)) {
+ value = namedColor;
+ return true;
+ }
+
+ // float color
+ var split = valueString.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
+ for (int i = 0; i < split.Length; i++) {
+ split[i] = split[i].Trim();
+ }
+
+ switch (split.Length) {
+ case 4:
+ if (!float.TryParse(split[4], out var alpha)) return false;
+ value.a = (byte) (alpha * 0xFF);
+ goto case 3;
+
+ case 3:
+ if (!float.TryParse(split[0], out var red)) return false;
+ if (!float.TryParse(split[1], out var green)) return false;
+ if (!float.TryParse(split[2], out var blue)) return false;
+
+ value.r = (byte) (red * 0xFF);
+ value.g = (byte) (green * 0xFF);
+ value.b = (byte) (blue * 0xFF);
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file