Fix scaling issues when part is created

• KSP rescales the model object back to 1,1,1 on start, so don't use that for the model that gets scaled
• Some refactoring to consolidate property IDs
• rename some classes because I am indecisive
• Add and Get methods for MaterialPropertyCollection
• Make an attempt at a scale culling fix
feature-multiSDF
Andrew Cassidy 4 years ago
parent 5560eee368
commit da1fbf0f2a

@ -47,6 +47,7 @@ PART
decalFront = Decal-Front
decalBack = Decal-Back
decalModel = Decal-Model
useBaseNormal = false

@ -48,6 +48,7 @@ PART
decalFront = Decal-Front
decalBack = Decal-Back
decalModel = Decal-Model
useBaseNormal = true
}

@ -49,8 +49,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="ConformalDecalConfig.cs" />
<Compile Include="ConformalDecalIconFixer.cs" />
<Compile Include="DecalConfig.cs" />
<Compile Include="DecalIconFixer.cs" />
<Compile Include="DecalPropertyIDs.cs" />
<Compile Include="MaterialModifiers\MaterialColorProperty.cs" />
<Compile Include="MaterialModifiers\MaterialFloatProperty.cs" />
<Compile Include="MaterialModifiers\MaterialProperty.cs" />

@ -2,7 +2,7 @@ using System.Collections.Generic;
using UnityEngine;
namespace ConformalDecals {
public static class ConformalDecalConfig {
public static class DecalConfig {
private static List<string> _shaderBlacklist;
public static bool IsBlacklisted(Shader shader) {

@ -3,7 +3,7 @@ using UnityEngine;
namespace ConformalDecals {
[KSPAddon(KSPAddon.Startup.EditorAny, true)]
public class ConformalDecalIconFixer : MonoBehaviour {
public class DecalIconFixer : MonoBehaviour {
private static readonly List<string> PartNames = new List<string>();
public static void QueuePart(string name) {
@ -11,12 +11,12 @@ namespace ConformalDecals {
}
public void Start() {
foreach (var name in PartNames) {
Debug.Log($"Unf*&king decal preview on {name}");
var partInfo = PartLoader.getPartInfoByName(name);
foreach (var partName in PartNames) {
Debug.Log($"Unf*&king decal preview on {partName}");
var partInfo = PartLoader.getPartInfoByName(partName);
if (partInfo == null) {
Debug.Log($"Part {name} not found!");
Debug.Log($"Part {partName} not found!");
continue;
}
@ -24,21 +24,16 @@ namespace ConformalDecals {
var decalModule = partInfo.partPrefab.FindModuleImplementing<ModuleConformalDecalBase>();
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");
Debug.Log($"Part {partName} has no frontTransform");
continue;
}
if (backTransform == null) {
Debug.Log($"Part {name} has no backTransform");
Debug.Log($"Part {partName} has no backTransform");
continue;
}
@ -52,7 +47,8 @@ namespace ConformalDecals {
}
backTransform.GetComponent<MeshRenderer>().material = decalModule.backMaterial;
frontTransform.GetComponent<MeshRenderer>().material = decalModule.materialProperties.PreviewMaterial;
if (decalModule.updateBackScale) {
backTransform.GetComponent<MeshRenderer>().material.SetTextureScale(PropertyIDs._MainTex, backScale);
}

@ -0,0 +1,15 @@
using UnityEngine;
// ReSharper disable InconsistentNaming
namespace ConformalDecals {
public static class DecalPropertyIDs {
public static readonly int _BumpMap = Shader.PropertyToID("_BumpMap");
public static readonly int _BumpMap_ST = Shader.PropertyToID("_BumpMap_ST");
public static readonly int _Cull = Shader.PropertyToID("_Cull");
public static readonly int _Cutoff = Shader.PropertyToID("_Cutoff");
public static readonly int _DecalNormal = Shader.PropertyToID("_DecalNormal");
public static readonly int _DecalOpacity = Shader.PropertyToID("_DecalOpacity");
public static readonly int _DecalTangent = Shader.PropertyToID("_DecalTangent");
public static readonly int _ProjectionMatrix = Shader.PropertyToID("_ProjectionMatrix");
}
}

@ -3,11 +3,19 @@ using System.Collections.Generic;
using System.Runtime.Serialization;
using UniLinq;
using UnityEngine;
using UnityEngine.Rendering;
namespace ConformalDecals.MaterialModifiers {
public class MaterialPropertyCollection : ScriptableObject, ISerializationCallbackReceiver {
private static readonly int OpacityId = Shader.PropertyToID("_DecalOpacity");
private static readonly int CutoffId = Shader.PropertyToID("_Cutoff");
[SerializeField] private Shader _shader;
[SerializeField] private MaterialTextureProperty _mainTexture;
[SerializeField] private string[] _serializedNames;
[SerializeField] private MaterialProperty[] _serializedProperties;
private Dictionary<string, MaterialProperty> _materialProperties;
private Material _decalMaterial;
private Material _previewMaterial;
public Shader DecalShader => _shader;
@ -16,6 +24,8 @@ namespace ConformalDecals.MaterialModifiers {
if (_decalMaterial == null) {
_decalMaterial = new Material(_shader);
UpdateMaterial(_decalMaterial);
_previewMaterial.SetInt(DecalPropertyIDs._Cull, (int) CullMode.Off);
}
return _decalMaterial;
@ -27,7 +37,9 @@ namespace ConformalDecals.MaterialModifiers {
if (_previewMaterial == null) {
_previewMaterial = new Material(_shader);
UpdateMaterial(_previewMaterial);
_previewMaterial.EnableKeyword("DECAL_PREVIEW");
_previewMaterial.SetInt(DecalPropertyIDs._Cull, (int) CullMode.Back);
}
return _previewMaterial;
@ -50,41 +62,31 @@ namespace ConformalDecals.MaterialModifiers {
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<string, MaterialProperty> _materialProperties;
private Material _decalMaterial;
private Material _previewMaterial;
public void OnBeforeSerialize() {
Debug.Log($"Serializing MaterialPropertyCollection {this.GetInstanceID()}");
if (_materialProperties == null) throw new SerializationException("Tried to serialize an unininitalized MaterialPropertyCollection");
if (_materialProperties == null) throw new SerializationException("Tried to serialize an uninitialized MaterialPropertyCollection");
_serializedIDs = _materialProperties.Keys.ToArray();
_serializedNames = _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 (_serializedNames == 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.");
if (_serializedProperties.Length != _serializedNames.Length) throw new SerializationException("Material property arrays are different lengths.");
_materialProperties ??= new Dictionary<string, MaterialProperty>();
for (var i = 0; i < _serializedIDs.Length; i++) {
for (var i = 0; i < _serializedNames.Length; i++) {
var property = MaterialProperty.Instantiate(_serializedProperties[i]);
Debug.Log($"insantiating {property.GetType().Name} {property.GetInstanceID()}");
_materialProperties.Add(_serializedIDs[i], property);
_materialProperties.Add(_serializedNames[i], property);
if (property is MaterialTextureProperty textureProperty) {
_mainTexture = textureProperty;
@ -98,7 +100,7 @@ namespace ConformalDecals.MaterialModifiers {
}
public void AddProperty(MaterialProperty property) {
if (property == null) throw new ArgumentNullException("Tried to add a null property");
if (property == null) throw new ArgumentNullException(nameof(property));
_materialProperties.Add(property.name, property);
@ -107,16 +109,61 @@ namespace ConformalDecals.MaterialModifiers {
}
}
public T AddProperty<T>(string propertyName) where T : MaterialProperty {
if (_materialProperties.ContainsKey(propertyName)) throw new ArgumentException("property with that name already exists!");
var newProperty = MaterialProperty.CreateInstance<T>();
newProperty.PropertyName = propertyName;
_materialProperties.Add(propertyName, newProperty);
return newProperty;
}
public T GetProperty<T>(string propertyName) where T : MaterialProperty {
if (_materialProperties.ContainsKey(propertyName) && _materialProperties[propertyName] is T property) {
return property;
}
else {
return null;
}
}
public T AddOrGetProperty<T>(string propertyName) where T : MaterialProperty {
if (_materialProperties.ContainsKey(propertyName) && _materialProperties[propertyName] is T property) {
return property;
}
else {
return AddProperty<T>(propertyName);
}
}
public MaterialTextureProperty AddTextureProperty(string propertyName, bool isMain = false) {
var newProperty = AddProperty<MaterialTextureProperty>(propertyName);
if (isMain) MainTexture = newProperty;
return newProperty;
}
public MaterialTextureProperty GetTextureProperty(string propertyName) {
return GetProperty<MaterialTextureProperty>(propertyName);
}
public MaterialTextureProperty AddOrGetTextureProperty(string propertyName, bool isMain = false) {
if (_materialProperties.ContainsKey(propertyName) && _materialProperties[propertyName] is MaterialTextureProperty property) {
return property;
}
else {
return AddTextureProperty(propertyName, isMain);
}
}
public void ParseProperty<T>(ConfigNode node) where T : MaterialProperty {
var name = node.GetValue("name");
if (string.IsNullOrEmpty(name)) throw new ArgumentException("node has no name");
var propertyName = node.GetValue("name");
if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("node has no name");
Debug.Log($"Parsing material property {name}");
Debug.Log($"Parsing material property {propertyName}");
T newProperty;
if (_materialProperties.ContainsKey(name)) {
if (_materialProperties[name] is T property) {
if (_materialProperties.ContainsKey(propertyName)) {
if (_materialProperties[propertyName] is T property) {
newProperty = property;
property.ParseNode(node);
}
@ -128,7 +175,7 @@ namespace ConformalDecals.MaterialModifiers {
newProperty = MaterialProperty.CreateInstance<T>();
Debug.Log($"Adding new material property of type {newProperty.GetType().Name} {newProperty.GetInstanceID()}");
newProperty.ParseNode(node);
_materialProperties.Add(name, newProperty);
_materialProperties.Add(propertyName, newProperty);
}
if (newProperty is MaterialTextureProperty textureProperty && textureProperty.isMain) {
@ -170,11 +217,13 @@ namespace ConformalDecals.MaterialModifiers {
}
public void SetOpacity(float opacity) {
DecalMaterial.SetFloat(OpacityId, opacity);
DecalMaterial.SetFloat(DecalPropertyIDs._DecalOpacity, opacity);
PreviewMaterial.SetFloat(DecalPropertyIDs._DecalOpacity, opacity);
}
public void SetCutoff(float cutoff) {
DecalMaterial.SetFloat(CutoffId, cutoff);
DecalMaterial.SetFloat(DecalPropertyIDs._Cutoff, cutoff);
PreviewMaterial.SetFloat(DecalPropertyIDs._Cutoff, cutoff);
}
public void UpdateMaterials() {
@ -195,8 +244,11 @@ namespace ConformalDecals.MaterialModifiers {
public void UpdateMaterial(Material material) {
if (material == null) throw new ArgumentNullException(nameof(material));
foreach (var entry in _materialProperties) {
Debug.Log($"Applying material property {entry.Key} {entry.Value.PropertyName} {entry.Value.GetInstanceID()}");
entry.Value.Modify(material);
}
}

@ -99,7 +99,7 @@ namespace ConformalDecals {
// find model transform
if (string.IsNullOrEmpty(decalModel)) {
decalModelTransform = part.transform.Find("model");
decalModelTransform = decalFrontTransform;
}
else {
decalModelTransform = part.FindModelTransform(decalModel);
@ -135,7 +135,7 @@ namespace ConformalDecals {
}
}
ConformalDecalIconFixer.QueuePart(part.name);
DecalIconFixer.QueuePart(part.name);
// set shader
materialProperties.SetShader(shader);
@ -149,6 +149,9 @@ namespace ConformalDecals {
UpdateScale();
UpdateProjection();
}
else {
UpdateScale();
}
}
public override void OnAwake() {
@ -176,7 +179,6 @@ namespace ConformalDecals {
materialProperties.SetRenderQueue(DecalQueue);
UpdateMaterials();
UpdateScale();
if (HighLogic.LoadedSceneIsGame) {
// set initial attachment state
@ -308,6 +310,9 @@ namespace ConformalDecals {
protected void UpdateMaterials() {
materialProperties.UpdateMaterials();
materialProperties.SetOpacity(opacity);
materialProperties.SetCutoff(cutoff);
_decalMaterial = materialProperties.DecalMaterial;
_previewMaterial = materialProperties.PreviewMaterial;
@ -345,7 +350,7 @@ namespace ConformalDecals {
if (renderer.gameObject.activeInHierarchy == false) continue;
// skip blacklisted shaders
if (ConformalDecalConfig.IsBlacklisted(renderer.material.shader)) continue;
if (DecalConfig.IsBlacklisted(renderer.material.shader)) continue;
var meshFilter = renderer.GetComponent<MeshFilter>();
if (meshFilter == null) continue; // object has a meshRenderer with no filter, invalid

@ -1,22 +1,13 @@
using ConformalDecals.MaterialModifiers;
using ConformalDecals.Util;
using UnityEngine;
namespace ConformalDecals {
public class ModuleConformalDecalFlag : ModuleConformalDecalBase {
[KSPField] public MaterialTextureProperty flagTextureProperty;
private const string defaultFlag = "Squad/Flags/default";
private const string DefaultFlag = "Squad/Flags/default";
public override void OnLoad(ConfigNode node) {
base.OnLoad(node);
if (HighLogic.LoadedSceneIsGame) {
UpdateFlag(EditorLogic.FlagURL != string.Empty ? EditorLogic.FlagURL : HighLogic.CurrentGame.flagURL);
}
else {
UpdateFlag(defaultFlag);
}
UpdateFlag(GetDefaultFlag());
}
public override void OnStart(StartState state) {
@ -25,6 +16,8 @@ namespace ConformalDecals {
if (HighLogic.LoadedSceneIsGame) {
GameEvents.onMissionFlagSelect.Add(UpdateFlag);
}
UpdateFlag(GetDefaultFlag());
}
public override void OnIconCreate() {
@ -37,6 +30,15 @@ namespace ConformalDecals {
base.OnDestroy();
}
private string GetDefaultFlag() {
if (HighLogic.LoadedSceneIsGame) {
return EditorLogic.FlagURL != string.Empty ? EditorLogic.FlagURL : HighLogic.CurrentGame.flagURL;
}
else {
return DefaultFlag;
}
}
private void UpdateFlag(string flagUrl) {
this.Log($"Loading flag texture '{flagUrl}'.");
var flagTexture = GameDatabase.Instance.GetTexture(flagUrl, false);
@ -45,18 +47,7 @@ namespace ConformalDecals {
return;
}
if (flagTextureProperty == null) {
this.Log("Initializing flag property");
flagTextureProperty = ScriptableObject.CreateInstance<MaterialTextureProperty>();
flagTextureProperty.PropertyName = "_Decal";
flagTextureProperty.isMain = true;
materialProperties.AddProperty(flagTextureProperty);
materialProperties.MainTexture = flagTextureProperty;
}
else { }
flagTextureProperty.texture = flagTexture;
materialProperties.AddOrGetTextureProperty("_Decal", true).texture = flagTexture;
UpdateMaterials();
}

@ -4,28 +4,28 @@ using UnityEngine.Rendering;
namespace ConformalDecals {
public class ProjectionTarget {
private static readonly int _projectionMatrixID = Shader.PropertyToID("_ProjectionMatrix");
private static readonly int _decalNormalID = Shader.PropertyToID("_DecalNormal");
private static readonly int _decalTangentID = Shader.PropertyToID("_DecalTangent");
// Target object data
public readonly Transform target;
private readonly Renderer _targetRenderer;
private readonly Mesh _targetMesh;
private bool _projectionEnabled;
private readonly int _cullMode;
// property block
private readonly MaterialPropertyBlock _decalMPB;
private static readonly int normalID = Shader.PropertyToID("_BumpMap");
private static readonly int normalIDST = Shader.PropertyToID("_BumpMap_ST");
public ProjectionTarget(MeshRenderer targetRenderer, Mesh targetMesh, bool useBaseNormal) {
target = targetRenderer.transform;
_targetRenderer = targetRenderer;
_targetMesh = targetMesh;
_decalMPB = new MaterialPropertyBlock();
var targetScale = target.lossyScale;
Debug.Log($"{target.name} scale = {targetScale}");
var targetDeterminant = (targetScale.x * targetScale.y * targetScale.z);
_cullMode = targetDeterminant < 0 ? (int) CullMode.Front : (int) CullMode.Back;
_decalMPB.SetInt(DecalPropertyIDs._Cull, 0);
}
public void Project(Matrix4x4 orthoMatrix, OrientedBounds projectorBounds, Transform projector, bool useBaseNormal) {
@ -40,21 +40,21 @@ 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(DecalPropertyIDs._ProjectionMatrix, projectionMatrix);
_decalMPB.SetVector(DecalPropertyIDs._DecalNormal, decalNormal);
_decalMPB.SetVector(DecalPropertyIDs._DecalTangent, decalTangent);
Debug.Log($"Projection enabled for {target.gameObject}");
if (useBaseNormal && targetMaterial.HasProperty(normalID)) {
var normal = targetMaterial.GetTexture(normalID);
if (useBaseNormal && targetMaterial.HasProperty(DecalPropertyIDs._BumpMap)) {
var normal = targetMaterial.GetTexture(DecalPropertyIDs._BumpMap);
if (normal != null) {
_decalMPB.SetTexture(normalID, targetMaterial.GetTexture(normalID));
_decalMPB.SetTexture(DecalPropertyIDs._BumpMap, targetMaterial.GetTexture(DecalPropertyIDs._BumpMap));
var normalScale = targetMaterial.GetTextureScale(normalID);
var normalOffset = targetMaterial.GetTextureOffset(normalID);
var normalScale = targetMaterial.GetTextureScale(DecalPropertyIDs._BumpMap);
var normalOffset = targetMaterial.GetTextureOffset(DecalPropertyIDs._BumpMap);
_decalMPB.SetVector(normalIDST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y));
_decalMPB.SetVector(DecalPropertyIDs._BumpMap_ST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y));
}
}
}

Loading…
Cancel
Save