Add tiling index and size values, for easier tiling

Also API changes made as I chased a bug
feature-multiSDF
Andrew Cassidy 4 years ago
parent f8d94ed681
commit 79bdc03c4b

@ -23,7 +23,6 @@ namespace ConformalDecals.MaterialModifiers {
get { get {
if (_decalMaterial == null) { if (_decalMaterial == null) {
_decalMaterial = new Material(_shader); _decalMaterial = new Material(_shader);
UpdateMaterial(_decalMaterial);
_decalMaterial.SetInt(DecalPropertyIDs._Cull, (int) CullMode.Off); _decalMaterial.SetInt(DecalPropertyIDs._Cull, (int) CullMode.Off);
} }
@ -36,7 +35,6 @@ namespace ConformalDecals.MaterialModifiers {
get { get {
if (_previewMaterial == null) { if (_previewMaterial == null) {
_previewMaterial = new Material(_shader); _previewMaterial = new Material(_shader);
UpdateMaterial(_previewMaterial);
_previewMaterial.EnableKeyword("DECAL_PREVIEW"); _previewMaterial.EnableKeyword("DECAL_PREVIEW");
_previewMaterial.SetInt(DecalPropertyIDs._Cull, (int) CullMode.Back); _previewMaterial.SetInt(DecalPropertyIDs._Cull, (int) CullMode.Back);
@ -79,7 +77,7 @@ namespace ConformalDecals.MaterialModifiers {
Debug.Log($"insantiating {property.GetType().Name} {property.GetInstanceID()}"); Debug.Log($"insantiating {property.GetType().Name} {property.GetInstanceID()}");
_materialProperties.Add(_serializedNames[i], property); _materialProperties.Add(_serializedNames[i], property);
if (property is MaterialTextureProperty textureProperty) { if (property is MaterialTextureProperty textureProperty && textureProperty.isMain) {
_mainTexture = textureProperty; _mainTexture = textureProperty;
} }
} }
@ -105,7 +103,7 @@ namespace ConformalDecals.MaterialModifiers {
_materialProperties.Add(property.name, property); _materialProperties.Add(property.name, property);
if (property is MaterialTextureProperty textureProperty) { if (property is MaterialTextureProperty textureProperty) {
if (textureProperty.isMain) _mainTexture ??= textureProperty; if (textureProperty.isMain) _mainTexture = textureProperty;
} }
} }
@ -114,6 +112,7 @@ namespace ConformalDecals.MaterialModifiers {
var newProperty = MaterialProperty.CreateInstance<T>(); var newProperty = MaterialProperty.CreateInstance<T>();
newProperty.PropertyName = propertyName; newProperty.PropertyName = propertyName;
_materialProperties.Add(propertyName, newProperty); _materialProperties.Add(propertyName, newProperty);
return newProperty; return newProperty;
} }
@ -137,7 +136,8 @@ namespace ConformalDecals.MaterialModifiers {
public MaterialTextureProperty AddTextureProperty(string propertyName, bool isMain = false) { public MaterialTextureProperty AddTextureProperty(string propertyName, bool isMain = false) {
var newProperty = AddProperty<MaterialTextureProperty>(propertyName); var newProperty = AddProperty<MaterialTextureProperty>(propertyName);
if (isMain) MainTexture = newProperty; if (isMain) _mainTexture = newProperty;
return newProperty; return newProperty;
} }
@ -146,41 +146,26 @@ namespace ConformalDecals.MaterialModifiers {
} }
public MaterialTextureProperty AddOrGetTextureProperty(string propertyName, bool isMain = false) { public MaterialTextureProperty AddOrGetTextureProperty(string propertyName, bool isMain = false) {
if (_materialProperties.ContainsKey(propertyName) && _materialProperties[propertyName] is MaterialTextureProperty property) { var newProperty = AddOrGetProperty<MaterialTextureProperty>(propertyName);
return property; if (isMain) _mainTexture = newProperty;
}
else { return newProperty;
return AddTextureProperty(propertyName, isMain);
}
} }
public void ParseProperty<T>(ConfigNode node) where T : MaterialProperty { public T ParseProperty<T>(ConfigNode node) where T : MaterialProperty {
var propertyName = node.GetValue("name"); var propertyName = node.GetValue("name");
if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("node has no name"); if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("node has no name");
Debug.Log($"Parsing material property {propertyName}"); Debug.Log($"Parsing material property {propertyName}");
T newProperty; var newProperty = AddOrGetProperty<T>(propertyName);
newProperty.ParseNode(node);
if (_materialProperties.ContainsKey(propertyName)) {
if (_materialProperties[propertyName] is T property) {
newProperty = property;
property.ParseNode(node);
}
else {
throw new ArgumentException("Material property already exists for {name} but it has a different type");
}
}
else {
newProperty = MaterialProperty.CreateInstance<T>();
Debug.Log($"Adding new material property of type {newProperty.GetType().Name} {newProperty.GetInstanceID()}");
newProperty.ParseNode(node);
_materialProperties.Add(propertyName, newProperty);
}
if (newProperty is MaterialTextureProperty textureProperty && textureProperty.isMain) { if (newProperty is MaterialTextureProperty textureProperty && textureProperty.isMain) {
Debug.Log("new texture has isMain enabled");
_mainTexture = textureProperty; _mainTexture = textureProperty;
} }
return newProperty;
} }
public void SetShader(string shaderName) { public void SetShader(string shaderName) {
@ -209,32 +194,35 @@ namespace ConformalDecals.MaterialModifiers {
public void UpdateScale(Vector2 scale) { public void UpdateScale(Vector2 scale) {
foreach (var entry in _materialProperties) { foreach (var entry in _materialProperties) {
if (entry.Value is MaterialTextureProperty textureProperty) { if (entry.Value is MaterialTextureProperty textureProperty && textureProperty.autoScale) {
textureProperty.UpdateScale(scale); textureProperty.SetScale(scale);
textureProperty.Modify(_decalMaterial);
textureProperty.Modify(_previewMaterial);
} }
} }
} }
public void UpdateTile(Rect tile) { public void UpdateTile(Rect tile) {
if (_mainTexture == null) throw new InvalidOperationException("UpdateTile called but no main texture is specified!"); if (_mainTexture == null) throw new InvalidOperationException("UpdateTile called but no main texture is specified!");
Vector2 scale; var mainTexSize = _mainTexture.Dimensions;
Vector2 offset;
scale.x = tile.width / _mainTexture.texture.width; Debug.Log($"Main texture is {_mainTexture.PropertyName} and its size is {mainTexSize}");
scale.y = tile.height / _mainTexture.texture.height;
offset.x = tile.x / _mainTexture.texture.width;
offset.y = tile.y / _mainTexture.texture.height;
foreach (var entry in _materialProperties) { foreach (var entry in _materialProperties) {
if (entry.Value is MaterialTextureProperty textureProperty) { if (entry.Value is MaterialTextureProperty textureProperty && textureProperty.autoTile) {
textureProperty.UpdateTiling(scale, offset); textureProperty.SetTile(tile, mainTexSize);
textureProperty.Modify(_decalMaterial);
textureProperty.Modify(_previewMaterial);
} }
} }
}
public void UpdateTile(int index, Vector2 tileSize) {
int tileCountX = (int) (_mainTexture.Width / tileSize.x);
int tileCountY = (int) (_mainTexture.Height / tileSize.y);
int x = index % tileCountX;
int y = index / tileCountY;
var tile = new Rect(x * tileSize.x, y * tileSize.y, tileSize.x, tileSize.y);
UpdateTile(tile);
} }
public void SetOpacity(float opacity) { public void SetOpacity(float opacity) {
@ -248,28 +236,14 @@ namespace ConformalDecals.MaterialModifiers {
} }
public void UpdateMaterials() { public void UpdateMaterials() {
if (_decalMaterial == null) { UpdateMaterial(DecalMaterial);
_decalMaterial = DecalMaterial; UpdateMaterial(PreviewMaterial);
}
else {
UpdateMaterial(_decalMaterial);
}
if (_previewMaterial == null) {
_previewMaterial = PreviewMaterial;
}
else {
UpdateMaterial(_previewMaterial);
}
} }
public void UpdateMaterial(Material material) { public void UpdateMaterial(Material material) {
if (material == null) throw new ArgumentNullException(nameof(material)); if (material == null) throw new ArgumentNullException(nameof(material));
foreach (var entry in _materialProperties) { foreach (var entry in _materialProperties) {
Debug.Log($"Applying material property {entry.Key} {entry.Value.PropertyName} {entry.Value.GetInstanceID()}");
entry.Value.Modify(material); entry.Value.Modify(material);
} }
} }

@ -2,40 +2,49 @@ using System;
using UnityEngine; using UnityEngine;
namespace ConformalDecals.MaterialModifiers { namespace ConformalDecals.MaterialModifiers {
public class MaterialTextureProperty : MaterialProperty { public class MaterialTextureProperty : MaterialProperty, ISerializationCallbackReceiver {
[SerializeField] public Texture2D texture;
[SerializeField] public bool isNormal; [SerializeField] public bool isNormal;
[SerializeField] public bool isMain; [SerializeField] public bool isMain;
[SerializeField] public bool autoScale; [SerializeField] public bool autoScale;
[SerializeField] public bool autoTile; [SerializeField] public bool autoTile;
[SerializeField] private bool _hasTile; [SerializeField] private string _textureUrl;
[SerializeField] private Rect _tileRect;
[SerializeField] private Vector2 _baseTextureScale = Vector2.one; private Texture2D _texture;
[SerializeField] private Vector2 _textureOffset = Vector2.zero;
[SerializeField] private Vector2 _textureScale = Vector2.one;
public float AspectRatio { [SerializeField] private bool _hasTile;
get { [SerializeField] private Rect _tileRect;
if (texture == null) return 1;
if (!_hasTile) return ((float) texture.height) / texture.width; [SerializeField] private Vector2 _scale = Vector2.one;
[SerializeField] private Vector2 _textureOffset;
[SerializeField] private Vector2 _textureScale = Vector2.one;
return _tileRect.height / _tileRect.width; public Texture2D Texture => _texture;
public string TextureUrl {
get => _textureUrl;
set {
_texture = LoadTexture(value, isNormal);
_textureUrl = value;
} }
} }
public Rect TileRect { public int Width => _texture.width;
get => _tileRect; public int Height => _texture.height;
set {
if (autoTile) return;
_hasTile = !(Mathf.Abs(value.width) < 0.1) || !(Mathf.Abs(value.height) < 0.1);
_tileRect = value; public int MaskedWidth => _hasTile ? (int) _tileRect.width : _texture.width;
UpdateTiling(); public int MaskedHeight => _hasTile ? (int) _tileRect.height : _texture.height;
}
public Vector2 Dimensions => new Vector2(_texture.width, _texture.height);
public Vector2 MaskedDimensions => _hasTile ? _tileRect.size : Dimensions;
public float AspectRatio => MaskedHeight / (float) MaskedWidth;
public void OnBeforeSerialize() { }
public void OnAfterDeserialize() {
// Unity appears to be screwing up textures when deserializing them, so this is the fix?
_texture = LoadTexture(_textureUrl, isNormal);
} }
public override void ParseNode(ConfigNode node) { public override void ParseNode(ConfigNode node) {
@ -46,68 +55,78 @@ namespace ConformalDecals.MaterialModifiers {
autoScale = ParsePropertyBool(node, "autoScale", true, autoScale); autoScale = ParsePropertyBool(node, "autoScale", true, autoScale);
autoTile = ParsePropertyBool(node, "autoTile", true, autoTile); autoTile = ParsePropertyBool(node, "autoTile", true, autoTile);
SetTexture(node.GetValue("textureUrl")); var textureUrl = node.GetValue("textureUrl");
if (node.HasValue("tileRect") && !autoTile) { if (string.IsNullOrEmpty(textureUrl)) {
TileRect = ParsePropertyRect(node, "tileRect", true, _tileRect); if (string.IsNullOrEmpty(_textureUrl)) {
} TextureUrl = "";
} }
public void SetTexture(string textureUrl) {
if ((textureUrl == null && isNormal) || textureUrl == "Bump") {
texture = Texture2D.normalTexture;
}
else if ((textureUrl == null && !isNormal) || textureUrl == "White") {
texture = Texture2D.whiteTexture;
}
else if (textureUrl == "Black") {
texture = Texture2D.blackTexture;
} }
else { else {
var textureInfo = GameDatabase.Instance.GetTextureInfo(textureUrl); TextureUrl = node.GetValue("textureUrl");
}
if (textureInfo == null) throw new Exception($"Cannot find texture: '{textureUrl}'");
Debug.Log($"parsed texture node with texture {_textureUrl}, {isMain}");
texture = isNormal ? textureInfo.normalMap : textureInfo.texture; if (node.HasValue("tileRect") && !autoTile) {
SetTile(ParsePropertyRect(node, "tileRect", true, _tileRect));
} }
if (texture == null) throw new Exception($"Cannot get texture from texture info '{textureUrl}', isNormalMap = {isNormal}");
UpdateTiling();
} }
public override void Modify(Material material) { public override void Modify(Material material) {
if (material == null) throw new ArgumentNullException(nameof(material)); if (material == null) throw new ArgumentNullException(nameof(material));
if (texture == null) { if (_texture == null) {
texture = Texture2D.whiteTexture; _texture = Texture2D.whiteTexture;
throw new NullReferenceException("texture is null, but should not be"); throw new NullReferenceException("texture is null, but should not be");
} }
material.SetTexture(_propertyID, texture); material.SetTexture(_propertyID, _texture);
material.SetTextureOffset(_propertyID, _textureOffset); material.SetTextureOffset(_propertyID, _textureOffset);
material.SetTextureScale(_propertyID, _textureScale); material.SetTextureScale(_propertyID, _textureScale * _scale);
} }
public void UpdateScale(Vector2 scale) { public void SetScale(Vector2 scale) {
if (autoScale) { _scale = scale;
_textureScale = _baseTextureScale * scale;
}
} }
public void UpdateTiling(Vector2 textureScale, Vector2 textureOffset) { public void SetTile(Rect tile) {
if (autoTile) { SetTile(tile, new Vector2(_texture.width, _texture.height));
_textureScale = textureScale;
_textureOffset = textureOffset;
}
} }
private void UpdateTiling() { public void SetTile(Rect tile, Vector2 mainTexDimensions) {
if (_hasTile) { var scale = tile.size;
_baseTextureScale.x = Mathf.Approximately(0, _tileRect.width) ? 1 : _tileRect.width / texture.width; var offset = tile.position;
_baseTextureScale.y = Mathf.Approximately(0, _tileRect.height) ? 1 : _tileRect.height / texture.height;
// invert y axis to deal with DXT image orientation
offset.y = mainTexDimensions.y - offset.y - tile.height;
// fit to given dimensions
scale /= mainTexDimensions;
offset /= mainTexDimensions;
_tileRect = tile;
_hasTile = true;
_textureScale = scale;
_textureOffset = offset;
}
private static Texture2D LoadTexture(string textureUrl, bool isNormal) {
Debug.Log($"loading texture '{textureUrl}', isNormalMap = {isNormal}");
if ((string.IsNullOrEmpty(textureUrl) && isNormal) || textureUrl == "Bump") {
return Texture2D.normalTexture;
} }
else {
_baseTextureScale = Vector2.one; if ((string.IsNullOrEmpty(textureUrl) && !isNormal) || textureUrl == "White") {
return Texture2D.whiteTexture;
} }
if (textureUrl == "Black") {
return Texture2D.blackTexture;
}
var texture = GameDatabase.Instance.GetTexture(textureUrl, isNormal);
if (texture == null) throw new Exception($"Cannot get texture '{textureUrl}', isNormalMap = {isNormal}");
return texture;
} }
} }
} }

@ -44,6 +44,10 @@ namespace ConformalDecals {
[KSPField] public Vector2 opacityRange = new Vector2(0, 1); [KSPField] public Vector2 opacityRange = new Vector2(0, 1);
[KSPField] public Vector2 cutoffRange = new Vector2(0, 1); [KSPField] public Vector2 cutoffRange = new Vector2(0, 1);
[KSPField] public Rect tileRect = new Rect(-1, -1, 0, 0);
[KSPField] public Vector2 tileSize;
[KSPField] public int tileIndex = -1;
[KSPField] public bool updateBackScale = true; [KSPField] public bool updateBackScale = true;
[KSPField] public bool useBaseNormal = true; [KSPField] public bool useBaseNormal = true;
@ -65,7 +69,6 @@ namespace ConformalDecals {
private Material _decalMaterial; private Material _decalMaterial;
private Material _previewMaterial; private Material _previewMaterial;
private int DecalQueue { private int DecalQueue {
get { get {
_decalQueueCounter++; _decalQueueCounter++;
@ -76,7 +79,7 @@ namespace ConformalDecals {
return _decalQueueCounter; return _decalQueueCounter;
} }
} }
public override void OnAwake() { public override void OnAwake() {
base.OnAwake(); base.OnAwake();
@ -87,12 +90,13 @@ namespace ConformalDecals {
materialProperties = ScriptableObject.Instantiate(materialProperties); materialProperties = ScriptableObject.Instantiate(materialProperties);
} }
} }
public override void OnLoad(ConfigNode node) { public override void OnLoad(ConfigNode node) {
this.Log("Loading module"); this.Log("Loading module");
this.Log($"{node.ToString()}");
try { try {
// SETUP TRANSFORMS // SETUP TRANSFORMS
// find front transform // find front transform
decalFrontTransform = part.FindModelTransform(decalFront); decalFrontTransform = part.FindModelTransform(decalFront);
if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'."); if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'.");
@ -147,12 +151,12 @@ namespace ConformalDecals {
} }
} }
} }
// PARSE MATERIAL PROPERTIES // PARSE MATERIAL PROPERTIES
// set shader // set shader
materialProperties.SetShader(shader); materialProperties.SetShader(shader);
// add texture nodes // add texture nodes
foreach (var textureNode in node.GetNodes("TEXTURE")) { foreach (var textureNode in node.GetNodes("TEXTURE")) {
materialProperties.ParseProperty<MaterialTextureProperty>(textureNode); materialProperties.ParseProperty<MaterialTextureProperty>(textureNode);
@ -167,15 +171,18 @@ namespace ConformalDecals {
foreach (var colorNode in node.GetNodes("COLOR")) { foreach (var colorNode in node.GetNodes("COLOR")) {
materialProperties.ParseProperty<MaterialColorProperty>(colorNode); materialProperties.ParseProperty<MaterialColorProperty>(colorNode);
} }
var tileString = node.GetValue("tile");
if (!string.IsNullOrEmpty(tileString)) {
var tileValid = ParseExtensions.TryParseRect(tileString, out var tile);
if (!tileValid) throw new FormatException($"Improperly formatted value for tile '{tileString}"); // handle texture tiling parameters
else {
materialProperties.UpdateTile(tile); this.Log($"TileRect {tileRect}");
} this.Log($"TileSize {tileSize}");
this.Log($"TileIndex {tileIndex}");
if (tileRect.x >= 0) {
materialProperties.UpdateTile(tileRect);
}
else if (tileIndex >= 0) {
materialProperties.UpdateTile(tileIndex, tileSize);
} }
// QUEUE PART FOR ICON FIXING IN VAB // QUEUE PART FOR ICON FIXING IN VAB
@ -191,7 +198,7 @@ namespace ConformalDecals {
UpdateProjection(); UpdateProjection();
} }
} }
public override void OnIconCreate() { public override void OnIconCreate() {
UpdateScale(); UpdateScale();
} }
@ -238,6 +245,7 @@ namespace ConformalDecals {
// scale or depth values have been changed, so update scale // scale or depth values have been changed, so update scale
// and update projection matrices if attached // and update projection matrices if attached
UpdateScale(); UpdateScale();
UpdateMaterials();
if (_isAttached) UpdateProjection(); if (_isAttached) UpdateProjection();
} }
@ -279,7 +287,6 @@ namespace ConformalDecals {
this.Log($"Decal attached to {part.parent.partName}"); this.Log($"Decal attached to {part.parent.partName}");
// hide preview model // hide preview model
decalFrontTransform.gameObject.SetActive(false); decalFrontTransform.gameObject.SetActive(false);
decalBackTransform.gameObject.SetActive(false); decalBackTransform.gameObject.SetActive(false);
@ -287,6 +294,7 @@ namespace ConformalDecals {
// add to preCull delegate // add to preCull delegate
Camera.onPreCull += Render; Camera.onPreCull += Render;
UpdateMaterials();
UpdateScale(); UpdateScale();
UpdateTargets(); UpdateTargets();
UpdateProjection(); UpdateProjection();
@ -302,12 +310,12 @@ namespace ConformalDecals {
// remove from preCull delegate // remove from preCull delegate
Camera.onPreCull -= Render; Camera.onPreCull -= Render;
UpdateMaterials();
UpdateScale(); UpdateScale();
} }
protected void UpdateScale() { protected void UpdateScale() {
var aspectRatio = materialProperties.AspectRatio; var aspectRatio = materialProperties.AspectRatio;
this.Log($"Aspect ratio is {aspectRatio}");
var size = new Vector2(scale, scale * aspectRatio); var size = new Vector2(scale, scale * aspectRatio);
// update orthogonal matrix scale // update orthogonal matrix scale

@ -36,13 +36,8 @@ namespace ConformalDecals {
private void UpdateFlag(string flagUrl) { private void UpdateFlag(string flagUrl) {
this.Log($"Loading flag texture '{flagUrl}'."); this.Log($"Loading flag texture '{flagUrl}'.");
var flagTexture = GameDatabase.Instance.GetTexture(flagUrl, false);
if (flagTexture == null) {
this.LogWarning($"Unable to find flag texture '{flagUrl}'.");
return;
}
materialProperties.AddOrGetTextureProperty("_Decal", true).texture = flagTexture; materialProperties.AddOrGetTextureProperty("_Decal", true).TextureUrl = flagUrl;
UpdateMaterials(); UpdateMaterials();
} }

@ -36,7 +36,6 @@ namespace ConformalDecals {
_decalMPB.SetMatrix(DecalPropertyIDs._ProjectionMatrix, projectionMatrix); _decalMPB.SetMatrix(DecalPropertyIDs._ProjectionMatrix, projectionMatrix);
_decalMPB.SetVector(DecalPropertyIDs._DecalNormal, decalNormal); _decalMPB.SetVector(DecalPropertyIDs._DecalNormal, decalNormal);
_decalMPB.SetVector(DecalPropertyIDs._DecalTangent, decalTangent); _decalMPB.SetVector(DecalPropertyIDs._DecalTangent, decalTangent);
Debug.Log($"Projection enabled for {target.gameObject}");
if (useBaseNormal && targetMaterial.HasProperty(DecalPropertyIDs._BumpMap)) { if (useBaseNormal && targetMaterial.HasProperty(DecalPropertyIDs._BumpMap)) {
var normal = targetMaterial.GetTexture(DecalPropertyIDs._BumpMap); var normal = targetMaterial.GetTexture(DecalPropertyIDs._BumpMap);
@ -53,7 +52,6 @@ namespace ConformalDecals {
} }
else { else {
_projectionEnabled = false; _projectionEnabled = false;
Debug.Log($"Projection disabled for {target.gameObject}");
} }
} }

Loading…
Cancel
Save