diff --git a/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/GameData/ConformalDecals/Plugins/ConformalDecals.dll
index 76e71ba..bcdc259 100644
Binary files a/GameData/ConformalDecals/Plugins/ConformalDecals.dll and b/GameData/ConformalDecals/Plugins/ConformalDecals.dll differ
diff --git a/Source/ConformalDecals/ConformalDecals.csproj b/Source/ConformalDecals/ConformalDecals.csproj
index b6b940e..fd1d246 100644
--- a/Source/ConformalDecals/ConformalDecals.csproj
+++ b/Source/ConformalDecals/ConformalDecals.csproj
@@ -66,6 +66,7 @@
+
diff --git a/Source/ConformalDecals/DecalConfig.cs b/Source/ConformalDecals/DecalConfig.cs
index e446b94..b0b8e78 100644
--- a/Source/ConformalDecals/DecalConfig.cs
+++ b/Source/ConformalDecals/DecalConfig.cs
@@ -1,13 +1,15 @@
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 = 3;
+
public static Texture2D BlankNormal => _blankNormal;
+ public static int DecalLayer => _decalLayer;
public static bool IsBlacklisted(Shader shader) {
return IsBlacklisted(shader.name);
@@ -22,6 +24,8 @@ namespace ConformalDecals {
foreach (var shaderName in blacklist.GetValuesList("shader")) {
_shaderBlacklist.Add(shaderName);
}
+
+ ParseUtil.ParseIntIndirect(ref _decalLayer, node, "decalLayer");
}
}
@@ -30,7 +34,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 +42,13 @@ namespace ConformalDecals {
tex.SetPixels32(colors);
}
}
+
tex.Apply();
return tex;
}
+ // ReSharper disable once UnusedMember.Global
public static void ModuleManagerPostLoad() {
_shaderBlacklist = new List();
@@ -55,6 +61,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/MaterialProperties/MaterialColorProperty.cs b/Source/ConformalDecals/MaterialProperties/MaterialColorProperty.cs
index 7a665e3..5b770e6 100644
--- a/Source/ConformalDecals/MaterialProperties/MaterialColorProperty.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialColorProperty.cs
@@ -1,18 +1,19 @@
using System;
+using ConformalDecals.Util;
using UnityEngine;
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/MaterialProperties/MaterialFloatProperty.cs b/Source/ConformalDecals/MaterialProperties/MaterialFloatProperty.cs
index db5c987..6745dfc 100644
--- a/Source/ConformalDecals/MaterialProperties/MaterialFloatProperty.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialFloatProperty.cs
@@ -1,4 +1,5 @@
using System;
+using ConformalDecals.Util;
using UnityEngine;
namespace ConformalDecals.MaterialProperties {
@@ -8,11 +9,11 @@ namespace ConformalDecals.MaterialProperties {
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
index 6416d22..6d792ab 100644
--- a/Source/ConformalDecals/MaterialProperties/MaterialProperty.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialProperty.cs
@@ -22,58 +22,5 @@ namespace ConformalDecals.MaterialProperties {
}
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/MaterialProperties/MaterialPropertyCollection.cs b/Source/ConformalDecals/MaterialProperties/MaterialPropertyCollection.cs
index ce464eb..b61912b 100644
--- a/Source/ConformalDecals/MaterialProperties/MaterialPropertyCollection.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialPropertyCollection.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
+using ConformalDecals.Util;
using UniLinq;
using UnityEngine;
using UnityEngine.Rendering;
@@ -163,8 +164,8 @@ namespace ConformalDecals.MaterialProperties {
}
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/MaterialProperties/MaterialTextureProperty.cs b/Source/ConformalDecals/MaterialProperties/MaterialTextureProperty.cs
index e1b229c..fbbeacf 100644
--- a/Source/ConformalDecals/MaterialProperties/MaterialTextureProperty.cs
+++ b/Source/ConformalDecals/MaterialProperties/MaterialTextureProperty.cs
@@ -1,4 +1,5 @@
using System;
+using ConformalDecals.Util;
using UnityEngine;
namespace ConformalDecals.MaterialProperties {
@@ -9,7 +10,7 @@ namespace ConformalDecals.MaterialProperties {
[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.MaterialProperties {
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/Util/ParseUtil.cs b/Source/ConformalDecals/Util/ParseUtil.cs
new file mode 100644
index 0000000..4cbddd3
--- /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[0], out var green)) return false;
+ if (!float.TryParse(split[0], 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