Big refactor to enable preview materials

• add shader variants for decal previewing
• start to add code for part icon
• refactor material properties to be serializable
todo:
• fix decal preview scale (need to call UpdateScale on detached state)
• fix texture preview in part icon
• adjust culling per-object when rendering (turns out cull and ztest values are used by unity at the render time, not by the shader itself, so they can be adjusted in material property blocks!)
feature-multiSDF
Andrew Cassidy 4 years ago
parent 6c20675a99
commit e9c8f3dafb

3
.gitignore vendored

@ -10,6 +10,9 @@ Logs/
Packages/
ProjectSettings/
Temp/
Distribution/GameData/ConformalDecals/Resources/Resources
Distribution/GameData/ConformalDecals/Resources/Resources.manifest
Distribution/GameData/ConformalDecals/Resources/conformaldecals.shab.manifest
# Unity Project Files
PartTools.cfg

@ -4,13 +4,17 @@ Shader "ConformalDecals/Feature/Bumped"
{
[Header(Texture Maps)]
_Decal("Decal Texture", 2D) = "gray" {}
_BumpMap("Decal Bump Map", 2D) = "bump" {}
_DecalBumpMap("Decal Bump Map", 2D) = "bump" {}
_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
_Opacity("_Opacity", Range(0,1) ) = 1
_DecalOpacity("Opacity", Range(0,1) ) = 1
_Background("Background Color", Color) = (0.9,0.9,0.9,0.7)
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 0
[Toggle(DECAL_PREVIEW)] _Preview ("Preview", Float) = 0
[Header(Effects)]
[PerRendererData]_Opacity("_Opacity", Range(0,1) ) = 1
[PerRendererData]_Opacity("_Opacity", Range(0,1) ) = 1
[PerRendererData]_RimFalloff("_RimFalloff", Range(0.01,5) ) = 0.1
[PerRendererData]_RimColor("_RimColor", Color) = (0,0,0,0)
[PerRendererData]_UnderwaterFogFactor ("Underwater Fog Factor", Range(0,1)) = 0
@ -18,9 +22,9 @@ Shader "ConformalDecals/Feature/Bumped"
SubShader
{
Tags { "Queue" = "Geometry+100" }
Cull Off
Zwrite Off
Ztest LEqual
Cull [_Cull]
Ztest LEqual
Pass
{
Name "FORWARD"
@ -32,15 +36,14 @@ Shader "ConformalDecals/Feature/Bumped"
#pragma fragment frag_forward
#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap
#pragma multi_compile DECAL_PROJECT DECAL_PREVIEW
sampler2D _Decal;
sampler2D _BumpMap;
sampler2D _DecalBumpMap;
float4 _Decal_ST;
float4 _BumpMap_ST;
float _Cutoff;
float _Opacity;
float4 _DecalBumpMap_ST;
float _RimFalloff;
float4 _RimColor;
@ -55,16 +58,18 @@ Shader "ConformalDecals/Feature/Bumped"
void surf (DecalSurfaceInput IN, inout SurfaceOutput o)
{
float4 color = tex2D(_Decal, IN.uv_decal);
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_bump));
float3 normal = UnpackNormal(tex2D(_DecalBumpMap, IN.uv_bump));
// clip alpha
clip(color.a - saturate(_Cutoff + 0.01));
#ifdef DECAL_PROJECT
// clip alpha
clip(color.a - _Cutoff + 0.01);
#endif //DECAL_PROJECT
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), normal));
float3 emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
o.Albedo = UnderwaterFog(IN.worldPosition, color).rgb;
o.Alpha = color.a * _Opacity;
o.Alpha = color.a * _DecalOpacity;
o.Emission = emission;
o.Normal = normal;
}
@ -83,15 +88,14 @@ Shader "ConformalDecals/Feature/Bumped"
#pragma fragment frag_forward
#pragma multi_compile_fwdadd nolightmap nodirlightmap nodynlightmap
#pragma multi_compile DECAL_PROJECT DECAL_PREVIEW
sampler2D _Decal;
sampler2D _BumpMap;
sampler2D _DecalBumpMap;
float4 _Decal_ST;
float4 _BumpMap_ST;
float4 _DecalBumpMap_ST;
float _Cutoff;
float _Opacity;
float _RimFalloff;
float4 _RimColor;
@ -106,16 +110,18 @@ Shader "ConformalDecals/Feature/Bumped"
void surf (DecalSurfaceInput IN, inout SurfaceOutput o)
{
float4 color = tex2D(_Decal, IN.uv_decal);
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_bump));
float3 normal = UnpackNormal(tex2D(_DecalBumpMap, IN.uv_bump));
// clip alpha
clip(color.a - saturate(_Cutoff + 0.01));
#ifdef DECAL_PROJECT
// clip alpha
clip(color.a - _Cutoff + 0.01);
#endif //DECAL_PROJECT
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), normal));
float3 emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
o.Albedo = UnderwaterFog(IN.worldPosition, color).rgb;
o.Alpha = color.a * _Opacity;
o.Alpha = color.a * _DecalOpacity;
o.Emission = emission;
o.Normal = normal;
}

@ -6,24 +6,27 @@ Shader "ConformalDecals/Paint/Diffuse"
_Decal("Decal Texture", 2D) = "gray" {}
_BumpMap("Bump Map", 2D) = "bump" {}
_EdgeWearStrength("Edge Wear Strength", Range(0,100)) = 0
_EdgeWearOffset("Edge Wear Offset", Range(0,1)) = 0
_EdgeWearStrength("Edge Wear Strength", Range(0,500)) = 100
_EdgeWearOffset("Edge Wear Offset", Range(0,1)) = 0.1
_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
_Opacity("_Opacity", Range(0,1) ) = 1
_DecalOpacity("Opacity", Range(0,1) ) = 1
_Background("Background Color", Color) = (0.9,0.9,0.9,0.7)
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 0
[Toggle(DECAL_PREVIEW)] _Preview ("Preview", Float) = 0
[Header(Effects)]
[PerRendererData]_Opacity("_Opacity", Range(0,1) ) = 1
[PerRendererData]_Opacity("_Opacity", Range(0,1) ) = 1
[PerRendererData]_RimFalloff("_RimFalloff", Range(0.01,5) ) = 0.1
[PerRendererData]_RimColor("_RimColor", Color) = (0,0,0,0)
[PerRendererData]_UnderwaterFogFactor ("Underwater Fog Factor", Range(0,1)) = 0
}
SubShader
{
Tags { "Queue" = "Geometry+400" }
ZWrite Off
ZTest LEqual
Offset -1, -1
Tags { "Queue" = "Geometry+100" }
Cull [_Cull]
Ztest LEqual
Pass
{
@ -36,18 +39,15 @@ Shader "ConformalDecals/Paint/Diffuse"
#pragma fragment frag_forward
#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap
#pragma multi_compile __ DECAL_PREVIEW
sampler2D _Decal;
sampler2D _BumpMap;
float4 _Decal_ST;
float4 _BumpMap_ST;
float _EdgeWearStrength;
float _EdgeWearOffset;
float _Cutoff;
float _Opacity;
float _RimFalloff;
float4 _RimColor;
@ -62,11 +62,10 @@ Shader "ConformalDecals/Paint/Diffuse"
void surf (DecalSurfaceInput IN, inout SurfaceOutput o)
{
float4 color = tex2D(_Decal, IN.uv_decal);
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_base));
// clip alpha
clip(color.a - _Cutoff);
decalClipAlpha(color.a);
float3 normal = IN.normal;
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), normal));
float3 emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
@ -76,9 +75,8 @@ Shader "ConformalDecals/Paint/Diffuse"
color.a *= saturate(1 + _EdgeWearOffset - saturate(_EdgeWearStrength * wearFactor));
o.Albedo = UnderwaterFog(IN.worldPosition, color).rgb;
o.Alpha = color.a * _Opacity;
o.Alpha = color.a * _DecalOpacity;
o.Emission = emission;
o.Normal = normal;
}
ENDCG
@ -95,18 +93,15 @@ Shader "ConformalDecals/Paint/Diffuse"
#pragma fragment frag_forward
#pragma multi_compile_fwdadd nolightmap nodirlightmap nodynlightmap
#pragma multi_compile __ DECAL_PREVIEW
sampler2D _Decal;
sampler2D _BumpMap;
float4 _Decal_ST;
float4 _BumpMap_ST;
float _EdgeWearStrength;
float _EdgeWearOffset;
float _Cutoff;
float _Opacity;
float _RimFalloff;
float4 _RimColor;
@ -121,11 +116,10 @@ Shader "ConformalDecals/Paint/Diffuse"
void surf (DecalSurfaceInput IN, inout SurfaceOutput o)
{
float4 color = tex2D(_Decal, IN.uv_decal);
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_base));
// clip alpha
clip(color.a - _Cutoff);
decalClipAlpha(color.a);
float3 normal = IN.normal;
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), normal));
float3 emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
@ -135,9 +129,8 @@ Shader "ConformalDecals/Paint/Diffuse"
color.a *= saturate(1 + _EdgeWearOffset - saturate(_EdgeWearStrength * wearFactor));
o.Albedo = UnderwaterFog(IN.worldPosition, color).rgb;
o.Alpha = color.a * _Opacity;
o.Alpha = color.a * _DecalOpacity;
o.Emission = emission;
o.Normal = normal;
}
ENDCG

@ -7,16 +7,19 @@ Shader "ConformalDecals/Paint/Specular"
_BumpMap("Bump Map", 2D) = "bump" {}
_SpecMap("Specular Map", 2D) = "black" {}
_EdgeWearStrength("Edge Wear Strength", Range(0,100)) = 0
_EdgeWearOffset("Edge Wear Offset", Range(0,1)) = 0
_EdgeWearStrength("Edge Wear Strength", Range(0,500)) = 100
_EdgeWearOffset("Edge Wear Offset", Range(0,1)) = 0.1
_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
_Opacity("_Opacity", Range(0,1) ) = 1
_DecalOpacity("Opacity", Range(0,1) ) = 1
_Background("Background Color", Color) = (0.9,0.9,0.9,0.7)
[Header(Specularity)]
_SpecColor ("_SpecColor", Color) = (0.5, 0.5, 0.5, 1)
_Shininess ("Shininess", Range (0.03, 10)) = 0.4
_SpecColor ("_SpecColor", Color) = (0.25, 0.25, 0.25, 1)
_Shininess ("Shininess", Range (0.03, 10)) = 0.3
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 0
[Toggle(DECAL_PREVIEW)] _Preview ("Preview", Float) = 0
[Header(Effects)]
[PerRendererData]_Opacity("_Opacity", Range(0,1) ) = 1
@ -26,10 +29,9 @@ Shader "ConformalDecals/Paint/Specular"
}
SubShader
{
Tags { "Queue" = "Geometry+400" }
ZWrite Off
ZTest LEqual
Offset -1, -1
Tags { "Queue" = "Geometry+100" }
Cull [_Cull]
Ztest LEqual
Pass
{
@ -42,22 +44,19 @@ Shader "ConformalDecals/Paint/Specular"
#pragma fragment frag_forward
#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap
#pragma multi_compile __ DECAL_PREVIEW
sampler2D _Decal;
sampler2D _BumpMap;
sampler2D _SpecMap;
float4 _Decal_ST;
float4 _BumpMap_ST;
float4 _SpecMap_ST;
float _EdgeWearStrength;
float _EdgeWearOffset;
half _Shininess;
float _Cutoff;
float _Opacity;
float _RimFalloff;
float4 _RimColor;
@ -73,11 +72,13 @@ Shader "ConformalDecals/Paint/Specular"
void surf (DecalSurfaceInput IN, inout SurfaceOutput o)
{
float4 color = tex2D(_Decal, IN.uv_decal);
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_base));
float3 specular = tex2D(_SpecMap, IN.uv_spec);
// clip alpha
clip(color.a - _Cutoff);
float3 normal = IN.normal;
#ifdef DECAL_PROJECT
// clip alpha
clip(color.a - _Cutoff + 0.01);
#endif //DECAL_PROJECT
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), normal));
float3 emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
@ -86,12 +87,11 @@ Shader "ConformalDecals/Paint/Specular"
float wearFactorAlpha = saturate(_EdgeWearStrength * wearFactor);
color.a *= saturate(1 + _EdgeWearOffset - saturate(_EdgeWearStrength * wearFactor));
color.a *= _Opacity;
color.a *= _DecalOpacity;
o.Albedo = UnderwaterFog(IN.worldPosition, color).rgb;
o.Alpha = color.a;
o.Emission = emission;
o.Normal = normal;
o.Specular = _Shininess;
o.Gloss = specular.r * color.a;
}
@ -110,22 +110,19 @@ Shader "ConformalDecals/Paint/Specular"
#pragma fragment frag_forward
#pragma multi_compile_fwdadd nolightmap nodirlightmap nodynlightmap
#pragma multi_compile __ DECAL_PREVIEW
sampler2D _Decal;
sampler2D _BumpMap;
sampler2D _SpecMap;
float4 _Decal_ST;
float4 _BumpMap_ST;
float4 _SpecMap_ST;
float _EdgeWearStrength;
float _EdgeWearOffset;
half _Shininess;
float _Cutoff;
float _Opacity;
float _RimFalloff;
float4 _RimColor;
@ -141,11 +138,13 @@ Shader "ConformalDecals/Paint/Specular"
void surf (DecalSurfaceInput IN, inout SurfaceOutput o)
{
float4 color = tex2D(_Decal, IN.uv_decal);
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_base));
float3 specular = tex2D(_SpecMap, IN.uv_spec);
float3 normal = IN.normal;
// clip alpha
clip(color.a - _Cutoff);
#ifdef DECAL_PROJECT
// clip alpha
clip(color.a - _Cutoff + 0.01);
#endif //DECAL_PROJECT
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), normal));
float3 emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
@ -156,9 +155,8 @@ Shader "ConformalDecals/Paint/Specular"
color.a *= saturate(1 + _EdgeWearOffset - saturate(_EdgeWearStrength * wearFactor));
o.Albedo = UnderwaterFog(IN.worldPosition, color).rgb;
o.Alpha = color.a * _Opacity;
o.Alpha = color.a * _DecalOpacity;
o.Emission = emission;
o.Normal = normal;
o.Specular = _Shininess;
o.Gloss = specular.r;
}

@ -18,10 +18,9 @@ struct DecalSurfaceInput
#endif //DECAL_EMISSIVE
#ifdef DECAL_BASE_NORMAL
float2 uv_base;
#endif //DECAL_BASE_NORMAL
float3 normal;
#endif
float3 normal;
float3 viewDir;
float3 worldPosition;
};
@ -30,10 +29,10 @@ struct appdata_decal
{
float4 vertex : POSITION;
float3 normal : NORMAL;
#ifdef DECAL_BASE_NORMAL
#if defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
#endif //DECAL_BASE_NORMAL
#endif
};
struct v2f
@ -65,6 +64,23 @@ float4x4 _ProjectionMatrix;
float3 _DecalNormal;
float3 _DecalTangent;
#ifdef DECAL_BASE_NORMAL
sampler2D _BumpMap;
float4 _BumpMap_ST;
#endif //DECAL_BASE_NORMAL
float _Cutoff;
float _DecalOpacity;
float _Opacity;
float4 _Background;
inline void decalClipAlpha(float alpha) {
#ifndef DECAL_PREVIEW
clip(alpha - _Cutoff + 0.01);
#endif
}
// declare surf function,
// this must be defined in any shader using this cginc
void surf (DecalSurfaceInput IN, inout SurfaceOutput o);
@ -76,7 +92,12 @@ v2f vert_forward(appdata_decal v)
o.pos = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
o.uv_decal = mul (_ProjectionMatrix, v.vertex);
#ifdef DECAL_PREVIEW
o.uv_decal = v.texcoord;
#else
o.uv_decal = mul (_ProjectionMatrix, v.vertex);
#endif //DECAL_PREVIEW
#ifdef DECAL_BASE_NORMAL
o.uv_base = TRANSFORM_TEX(v.texcoord, _BumpMap);
@ -85,7 +106,7 @@ v2f vert_forward(appdata_decal v)
float3 worldPosition = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
#ifdef DECAL_BASE_NORMAL
#if defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
// use tangent of base geometry
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
@ -95,7 +116,7 @@ v2f vert_forward(appdata_decal v)
fixed3 decalTangent = UnityObjectToWorldDir(_DecalTangent);
fixed3 worldBinormal = cross(decalTangent, worldNormal);
fixed3 worldTangent = cross(worldNormal, worldBinormal);
#endif //DECAL_BASE_NORMAL
#endif //defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPosition.x);
o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPosition.y);
@ -148,22 +169,26 @@ fixed4 frag_forward(v2f IN) : SV_Target
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPosition));
float3 viewDir = _unity_tbn_0 * worldViewDir.x + _unity_tbn_1 * worldViewDir.y + _unity_tbn_2 * worldViewDir.z;
// perform decal projection
fixed4 uv_projected = UNITY_PROJ_COORD(IN.uv_decal);
#ifdef DECAL_PREVIEW
fixed4 uv_projected = IN.uv_decal;
#else
// perform decal projection
fixed4 uv_projected = UNITY_PROJ_COORD(IN.uv_decal);
// clip texture outside of xyz bounds
clip(uv_projected.xyz);
clip(1-uv_projected.xyz);
// clip backsides
clip(dot(_DecalNormal, IN.normal));
// clip texture outside of xyz bounds
clip(uv_projected.xyz);
clip(1-uv_projected.xyz);
// clip backsides
clip(dot(_DecalNormal, IN.normal));
#endif //DECAL_PREVIEW
// initialize surface input
UNITY_INITIALIZE_OUTPUT(DecalSurfaceInput, i)
i.uv_decal = TRANSFORM_TEX(uv_projected, _Decal);
#ifdef DECAL_NORMAL
i.uv_bump = TRANSFORM_TEX(uv_projected, _BumpMap);
i.uv_bump = TRANSFORM_TEX(uv_projected, _DecalBumpMap);
#endif //DECAL_NORMAL
#ifdef DECAL_SPECULAR
@ -173,12 +198,16 @@ fixed4 frag_forward(v2f IN) : SV_Target
#ifdef DECAL_EMISSIVE
i.uv_glow = TRANSFORM_TEX(uv_projected, _GlowMap);
#endif //DECAL_EMISSIVE
#ifdef DECAL_BASE_NORMAL
i.uv_base = IN.uv_base;
#endif //DECAL_BASE_NORMAL
#ifdef DECAL_PREVIEW
i.normal = fixed3(0,0,1);
#else
i.normal = UnpackNormal(tex2D(_BumpMap, IN.uv_base));
#endif //DECAL_PREVIEW
#endif //DECAL_BASE_NORMAL
i.normal = IN.normal;
//i.normal = IN.normal;
i.viewDir = viewDir;
i.worldPosition = worldPosition;
@ -192,6 +221,14 @@ fixed4 frag_forward(v2f IN) : SV_Target
// call surface function
surf(i, o);
#ifdef DECAL_PREVIEW
o.Albedo = lerp(_Background.rgb,o.Albedo, o.Alpha);
o.Normal = lerp(float3(0,0,1), o.Normal, o.Alpha);
o.Gloss = lerp(_Background.a, o.Gloss, o.Alpha);
o.Emission = lerp(0, o.Emission, o.Alpha);
o.Alpha = _Opacity;
#endif //DECAL_PREVIEW
// compute lighting & shadowing factor
UNITY_LIGHT_ATTENUATION(atten, IN, worldPosition)
@ -203,7 +240,6 @@ fixed4 frag_forward(v2f IN) : SV_Target
WorldNormal.z = dot(_unity_tbn_2, o.Normal);
WorldNormal = normalize(WorldNormal);
o.Normal = WorldNormal;
//KSP lighting function
c += LightingBlinnPhongSmooth(o, lightDir, worldViewDir, atten);

@ -64,18 +64,18 @@ PART
name = ModuleConformalDecalGeneric
}
DATA {
decalShader = ConformalDecals/Feature/Bumped
shader = ConformalDecals/Feature/Bumped
TEXTURE {
name = _Decal
textureURL = ConformalDecals/Assets/Tortilla-diffuse
textureUrl = ConformalDecals/Assets/Tortilla-diffuse
isMain = true
}
TEXTURE
{
name = _BumpMap
textureURL = ConformalDecals/Assets/Tortilla-normal
name = _DecalBumpMap
textureUrl = ConformalDecals/Assets/Tortilla-normal
isNormalMap = true
}
}
@ -90,11 +90,11 @@ PART
name = ModuleConformalDecalGeneric
}
DATA {
decalShader = ConformalDecals/Paint/Diffuse
shader = ConformalDecals/Paint/Diffuse
useBaseNormal = true
TEXTURE {
name = _Decal
textureURL = ConformalDecals/Assets/Sign-HighVoltage-2
textureUrl = ConformalDecals/Assets/Sign-HighVoltage-2
isMain = true
}
}

@ -3,18 +3,18 @@ using UnityEngine;
namespace ConformalDecals.MaterialModifiers {
public class MaterialColorProperty : MaterialProperty {
private readonly Color _color;
[SerializeField] public Color color;
public MaterialColorProperty(ConfigNode node) : base(node) {
_color = ParsePropertyColor(node, "color", false);
}
public override void ParseNode(ConfigNode node) {
base.ParseNode(node);
public MaterialColorProperty(string name, Color value) : base(name) {
_color = value;
color = ParsePropertyColor(node, "color", true, color);
}
public override void Modify(Material material) {
material.SetColor(_propertyID, _color);
if (material == null) throw new ArgumentNullException("material cannot be null");
material.SetColor(_propertyID, color);
}
}
}

@ -3,18 +3,18 @@ using UnityEngine;
namespace ConformalDecals.MaterialModifiers {
public class MaterialFloatProperty : MaterialProperty {
private readonly float _value;
[SerializeField] public float value;
public MaterialFloatProperty(ConfigNode node) : base(node) {
_value = ParsePropertyFloat(node, "value", false);
}
public override void ParseNode(ConfigNode node) {
base.ParseNode(node);
public MaterialFloatProperty(string name, float value) : base(name) {
_value = value;
value = ParsePropertyFloat(node, "value", true, value);
}
public override void Modify(Material material) {
material.SetFloat(_propertyID, _value);
if (material == null) throw new ArgumentNullException("material cannot be null");
material.SetFloat(_propertyID, value);
}
}
}

@ -1,24 +1,24 @@
using System;
using UnityEngine;
using Object = UnityEngine.Object;
namespace ConformalDecals.MaterialModifiers {
public abstract class MaterialProperty {
public string PropertyName { get; }
protected readonly int _propertyID;
protected MaterialProperty(ConfigNode node) : this(node.GetValue("name")) { }
public abstract class MaterialProperty : ScriptableObject {
public string Name {
get => _propertyName;
set {
_propertyName = value;
_propertyID = Shader.PropertyToID(_propertyName);
}
}
protected MaterialProperty(string name) {
if (name == null)
throw new FormatException("name not found, cannot create material modifier");
[SerializeField] protected int _propertyID;
[SerializeField] protected string _propertyName;
if (name == string.Empty)
throw new FormatException("name is empty, cannot create material modifier");
public virtual void ParseNode(ConfigNode node) {
if (node == null) throw new ArgumentNullException("node cannot be null");
PropertyName = name;
_propertyID = Shader.PropertyToID(PropertyName);
Name = node.GetValue("name");
}
public abstract void Modify(Material material);
@ -57,10 +57,10 @@ namespace ConformalDecals.MaterialModifiers {
}
else {
if (valueString == null)
throw new FormatException($"Missing {typeof(T)} value for {valueName} in property '{PropertyName}'");
throw new FormatException($"Missing {typeof(T)} value for {valueName} in property '{Name}'");
if (valueString == string.Empty)
throw new FormatException($"Empty {typeof(T)} value for {valueName} in property '{PropertyName}'");
throw new FormatException($"Empty {typeof(T)} value for {valueName} in property '{Name}'");
}
if (tryParse(valueString, out var value)) {
@ -72,7 +72,7 @@ namespace ConformalDecals.MaterialModifiers {
}
else {
throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} in property '{PropertyName}' : '{valueString}");
throw new FormatException($"Improperly formatted {typeof(T)} value for {valueName} in property '{Name}' : '{valueString}");
}
}
}

@ -4,18 +4,18 @@ using UnityEngine;
namespace ConformalDecals.MaterialModifiers {
public class MaterialPropertyCollection : ScriptableObject {
private static readonly int OpacityId = Shader.PropertyToID("_Opacity");
private static readonly int OpacityId = Shader.PropertyToID("_DecalOpacity");
private static readonly int CutoffId = Shader.PropertyToID("_Cutoff");
public MaterialTextureProperty MainMaterialTextureProperty { get; set; }
public MaterialTextureProperty MainMaterialTextureProperty => _mainTexture;
private List<MaterialProperty> _materialProperties;
private List<MaterialTextureProperty> _textureMaterialProperties;
public Shader DecalShader => _shader;
public Material DecalMaterial {
get {
Debug.Log($"{_textureMaterialProperties == null}");
if (_decalMaterial == null) {
_decalMaterial = new Material(_decalShader);
_decalMaterial = new Material(_shader);
UpdateMaterial(_decalMaterial);
}
@ -23,13 +23,32 @@ namespace ConformalDecals.MaterialModifiers {
}
}
public Shader DecalShader => _decalShader;
public Material PreviewMaterial {
get {
if (_previewMaterial == null) {
_previewMaterial = new Material(_shader);
UpdateMaterial(_previewMaterial);
_previewMaterial.EnableKeyword("DECAL_PREVIEW");
}
public float AspectRatio => MainMaterialTextureProperty?.AspectRatio ?? 1f;
return _previewMaterial;
}
}
public float AspectRatio {
get {
if (MainMaterialTextureProperty == null) return 1;
return MainMaterialTextureProperty.AspectRatio;
}
}
[SerializeField] private Shader _decalShader;
[SerializeField] private Shader _shader;
[SerializeField] private List<MaterialProperty> _materialProperties;
[SerializeField] private List<MaterialTextureProperty> _textureMaterialProperties;
[SerializeField] private MaterialTextureProperty _mainTexture;
private Material _decalMaterial;
private Material _previewMaterial;
public void Initialize() {
_materialProperties = new List<MaterialProperty>();
@ -44,7 +63,7 @@ namespace ConformalDecals.MaterialModifiers {
}
foreach (var p in _materialProperties) {
if (p.PropertyName == property.PropertyName) {
if (p.Name == property.Name) {
_materialProperties.Remove(property);
}
}
@ -53,20 +72,20 @@ namespace ConformalDecals.MaterialModifiers {
if (property is MaterialTextureProperty textureProperty) {
foreach (var p in _textureMaterialProperties) {
if (p.PropertyName == textureProperty.PropertyName) {
if (p.Name == textureProperty.Name) {
_textureMaterialProperties.Remove(textureProperty);
}
}
_textureMaterialProperties.Add(textureProperty);
if (textureProperty.IsMain) MainMaterialTextureProperty ??= textureProperty;
if (textureProperty.isMain) _mainTexture ??= textureProperty;
}
}
public void SetShader(string shaderName) {
if (string.IsNullOrEmpty(shaderName)) {
if (_decalShader == null) {
if (_shader == null) {
Debug.Log("Using default decal shader");
shaderName = "ConformalDecals/Paint/Diffuse";
}
@ -79,13 +98,15 @@ namespace ConformalDecals.MaterialModifiers {
if (shader == null) throw new FormatException($"Unable to find specified shader '{shaderName}'");
_decalShader = shader;
_shader = shader;
_decalMaterial = null;
_previewMaterial = null;
}
public void SetRenderQueue(int queue) {
DecalMaterial.renderQueue = queue;
}
public void SetScale(Vector2 scale) {
foreach (var textureProperty in _textureMaterialProperties) {
textureProperty.UpdateScale(DecalMaterial, scale);
@ -101,10 +122,21 @@ namespace ConformalDecals.MaterialModifiers {
}
public void UpdateMaterials() {
if (_decalMaterial == null) {
_decalMaterial = DecalMaterial;
}
if (_previewMaterial == null) {
_previewMaterial = PreviewMaterial;
}
UpdateMaterial(_decalMaterial);
UpdateMaterial(_previewMaterial);
}
public void UpdateMaterial(Material material) {
if (material == null) throw new ArgumentNullException("material cannot be null");
foreach (var property in _materialProperties) {
property.Modify(material);
}

@ -3,29 +3,54 @@ using UnityEngine;
namespace ConformalDecals.MaterialModifiers {
public class MaterialTextureProperty : MaterialProperty {
public Texture2D texture;
[SerializeField] public Texture2D texture;
[SerializeField] public bool isNormal;
[SerializeField] public bool isMain;
[SerializeField] public bool autoScale;
[SerializeField] private bool _hasTile;
[SerializeField] private Rect _tileRect;
[SerializeField] private Vector2 _textureOffset = Vector2.zero;
[SerializeField] private Vector2 _textureScale = Vector2.one;
public float AspectRatio {
get {
if (texture == null) return 1;
if (!_hasTile || Mathf.Approximately(0, _tileRect.width)) return ((float) texture.height) / ((float) texture.width);
return _tileRect.height / _tileRect.width;
}
}
public bool IsNormal { get; }
public bool IsMain { get; }
public bool AutoScale { get; }
public Rect TileRect {
get => _tileRect;
set {
_hasTile = !(Mathf.Abs(value.width) < 0.1) || !(Mathf.Abs(value.height) < 0.1);
private readonly Rect _tileRect;
_tileRect = value;
UpdateTiling();
}
}
public float AspectRatio => _tileRect.height / _tileRect.width;
public override void ParseNode(ConfigNode node) {
base.ParseNode(node);
private readonly Vector2 _textureOffset;
private readonly Vector2 _textureScale;
isNormal = ParsePropertyBool(node, "isNormalMap", true, (Name == "_BumpMap") || isNormal);
isMain = ParsePropertyBool(node, "isMain", true, isMain);
autoScale = ParsePropertyBool(node, "autoScale", true, autoScale);
public MaterialTextureProperty(ConfigNode node) : base(node) {
IsNormal = ParsePropertyBool(node, "isNormalMap", true, PropertyName == "_BumpMap");
IsMain = ParsePropertyBool(node, "isMain", true);
AutoScale = ParsePropertyBool(node, "autoScale", true);
var textureUrl = node.GetValue("textureURL");
SetTexture(node.GetValue("textureUrl"));
if (node.HasValue("tileRect")) {
TileRect = ParsePropertyRect(node, "tileRect", true, _tileRect);
}
}
if ((textureUrl == null && IsNormal) || textureUrl == "Bump") {
public void SetTexture(string textureUrl) {
if ((textureUrl == null && isNormal) || textureUrl == "Bump") {
texture = Texture2D.normalTexture;
}
else if ((textureUrl == null && !IsNormal) || textureUrl == "White") {
else if ((textureUrl == null && !isNormal) || textureUrl == "White") {
texture = Texture2D.whiteTexture;
}
else if (textureUrl == "Black") {
@ -36,48 +61,43 @@ namespace ConformalDecals.MaterialModifiers {
if (textureInfo == null) throw new Exception($"Cannot find texture: '{textureUrl}'");
texture = IsNormal ? textureInfo.normalMap : textureInfo.texture;
texture = isNormal ? textureInfo.normalMap : textureInfo.texture;
}
if (texture == null) throw new Exception($"Cannot get texture from texture info '{textureUrl}' isNormalMap = {IsNormal}");
_tileRect = ParsePropertyRect(node, "tileRect", true, new Rect(0, 0, texture.width, texture.height));
_textureScale.x = _tileRect.width / texture.width;
_textureScale.y = _tileRect.height / texture.height;
_textureOffset.x = _tileRect.x / texture.width;
_textureOffset.y = _tileRect.y / texture.height;
if (texture == null) throw new Exception($"Cannot get texture from texture info '{textureUrl}', isNormalMap = {isNormal}");
UpdateTiling();
}
public MaterialTextureProperty(string name, Texture2D texture, Rect tileRect = default,
bool isNormal = false, bool isMain = false, bool autoScale = false) : base(name) {
this.texture = texture;
_tileRect = tileRect == default ? new Rect(0, 0, this.texture.width, this.texture.height) : tileRect;
IsNormal = isNormal;
IsMain = isMain;
AutoScale = autoScale;
_textureScale.x = _tileRect.width / this.texture.width;
_textureScale.y = _tileRect.height / this.texture.height;
_textureOffset.x = _tileRect.x / this.texture.width;
_textureOffset.y = _tileRect.y / this.texture.height;
}
public override void Modify(Material material) {
if (material == null) throw new ArgumentNullException(nameof(material));
if (texture == null) {
texture = Texture2D.whiteTexture;
throw new NullReferenceException("texture is null, but should not be");
}
material.SetTexture(_propertyID, texture);
material.SetTextureOffset(_propertyID, _textureOffset);
material.SetTextureScale(_propertyID, _textureScale);
}
public void UpdateScale(Material material, Vector2 scale) {
if (AutoScale) {
if (autoScale) {
material.SetTextureScale(_propertyID, new Vector2(_textureScale.x * scale.x, _textureScale.y * scale.y));
}
}
private void UpdateTiling() {
if (_hasTile) {
_textureScale.x = Mathf.Approximately(0, _tileRect.width) ? 1 : _tileRect.width / texture.width;
_textureScale.y = Mathf.Approximately(0, _tileRect.height) ? 1 : _tileRect.height / texture.height;
_textureOffset.x = _tileRect.x / texture.width;
_textureOffset.y = _tileRect.y / texture.height;
}
else {
_textureScale = Vector2.one;
_textureOffset = Vector2.zero;
}
}
}
}

@ -7,26 +7,27 @@ using UnityEngine;
namespace ConformalDecals {
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)]
UI_FloatRange(stepIncrement = 0.05f)]
public float scale = 1.0f;
[KSPField(guiName = "#LOC_ConformalDecals_gui-depth", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "F2", guiUnits = "m"),
UI_FloatRange(minValue = 0.05f, maxValue = 4f, stepIncrement = 0.05f)]
public float depth = 1.0f;
UI_FloatRange(stepIncrement = 0.02f)]
public float depth = 0.2f;
[KSPField(guiName = "#LOC_ConformalDecals_gui-opacity", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "P0"),
UI_FloatRange(minValue = 0.0f, maxValue = 1f, stepIncrement = 0.05f)]
UI_FloatRange(stepIncrement = 0.05f)]
public float opacity = 1.0f;
[KSPField(guiName = "#LOC_ConformalDecals_gui-cutoff", guiActive = false, guiActiveEditor = true, isPersistant = true, guiFormat = "P0"),
UI_FloatRange(minValue = 0.0f, maxValue = 1f, stepIncrement = 0.05f)]
UI_FloatRange(stepIncrement = 0.05f)]
public float cutoff = 0.5f;
[KSPField] public string shader = "ConformalDecals/Paint/Diffuse";
[KSPField] public string decalFront = string.Empty;
[KSPField] public string decalBack = string.Empty;
[KSPField] public string decalModel = string.Empty;
[KSPField] public string decalProjector = string.Empty;
[KSPField] public string decalShader = "ConformalDecals/Paint/Diffuse";
[KSPField] public Transform decalFrontTransform;
[KSPField] public Transform decalBackTransform;
@ -38,11 +39,10 @@ namespace ConformalDecals {
[KSPField] public bool opacityAdjustable = true;
[KSPField] public bool cutoffAdjustable = true;
[KSPField] public Vector2 scaleRange = new Vector2(0, 4);
[KSPField] public Vector2 depthRange = new Vector2(0, 4);
[KSPField] public Vector2 opacityRange = new Vector2(0, 1);
[KSPField] public Vector2 cutoffRange = new Vector2(0, 1);
[KSPField] public Vector2 decalQueueRange = new Vector2(2100, 2400);
[KSPField] public Vector2 scaleRange = new Vector2(0, 4);
[KSPField] public Vector2 depthRange = new Vector2(0, 2);
[KSPField] public Vector2 opacityRange = new Vector2(0, 1);
[KSPField] public Vector2 cutoffRange = new Vector2(0, 1);
[KSPField] public bool updateBackScale = true;
[KSPField] public bool useBaseNormal = true;
@ -50,7 +50,10 @@ namespace ConformalDecals {
[KSPField] public MaterialPropertyCollection materialProperties;
[KSPField] public Material backMaterial;
[KSPField] public Vector2 backTextureBaseScale;
private const int DecalQueueMin = 2100;
private const int DecalQueueMax = 2400;
private static int _decalQueueCounter = -1;
private List<ProjectionTarget> _targets;
@ -58,13 +61,16 @@ namespace ConformalDecals {
private bool _isAttached;
private Matrix4x4 _orthoMatrix;
private Bounds _decalBounds;
private Vector2 _backTextureBaseScale;
private Material _decalMaterial;
private Material _previewMaterial;
private int DecalQueue {
get {
_decalQueueCounter++;
if (_decalQueueCounter > decalQueueRange.y || _decalQueueCounter < decalQueueRange.x) {
_decalQueueCounter = (int) decalQueueRange.x;
if (_decalQueueCounter > DecalQueueMax || _decalQueueCounter < DecalQueueMin) {
_decalQueueCounter = DecalQueueMin;
}
return _decalQueueCounter;
@ -74,10 +80,6 @@ namespace ConformalDecals {
public override void OnLoad(ConfigNode node) {
this.Log("Loading module");
try {
if (HighLogic.LoadedSceneIsEditor) {
UpdateTweakables();
}
// find front transform
decalFrontTransform = part.FindModelTransform(decalFront);
if (decalFrontTransform == null) throw new FormatException($"Could not find decalFront transform: '{decalFront}'.");
@ -112,32 +114,25 @@ namespace ConformalDecals {
decalProjectorTransform = part.FindModelTransform(decalProjector);
if (decalProjectorTransform == null) throw new FormatException($"Could not find decalProjector transform: '{decalProjector}'.");
}
// get back material if necessary
if (updateBackScale) {
this.Log("Getting material and base scale for back material");
var backRenderer = decalBackTransform.GetComponent<MeshRenderer>();
if (backRenderer == null) {
this.LogError($"Specified decalBack transform {decalBack} has no renderer attached! Setting updateBackScale to false.");
updateBackScale = false;
}
else {
backMaterial = backRenderer.material;
if (backMaterial == 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);
}
}
if (HighLogic.LoadedSceneIsEditor) {
UpdateTweakables();
}
// update EVERYTHING if currently attached
if (_isAttached) {
UpdateScale();
UpdateTargets();
// setup material properties
if (HighLogic.LoadedSceneIsGame) {
// additional load, in flight or in the editor
materialProperties = ScriptableObject.Instantiate(materialProperties);
}
else {
// first load, so get everything set up
materialProperties = ScriptableObject.CreateInstance<MaterialPropertyCollection>();
materialProperties.Initialize();
}
// set shader
materialProperties.SetShader(shader);
}
catch (Exception e) {
this.LogException("Exception parsing partmodule", e);
@ -147,6 +142,7 @@ 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);
@ -154,21 +150,22 @@ namespace ConformalDecals {
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
materialProperties.SetRenderQueue(DecalQueue);
// clone materialProperties and setup queue
if (HighLogic.LoadedSceneIsGame) {
materialProperties = ScriptableObject.Instantiate(materialProperties);
materialProperties.SetRenderQueue(DecalQueue);
// set initial attachment state
if (part.parent == null) {
OnDetach();
}
else {
OnAttach();
// set initial attachment state
if (part.parent == null) {
_isAttached = false;
}
else {
OnAttach();
}
}
UpdateMaterials();
UpdateScale();
}
public void OnDestroy() {
@ -179,25 +176,25 @@ namespace ConformalDecals {
Camera.onPreCull -= Render;
}
private void OnSizeTweakEvent(BaseField field, object obj) {
protected void OnSizeTweakEvent(BaseField field, object obj) {
// scale or depth values have been changed, so update scale
// and update projection matrices if attached
UpdateScale();
if (_isAttached) UpdateProjection();
}
private void OnMaterialTweakEvent(BaseField field, object obj) {
protected void OnMaterialTweakEvent(BaseField field, object obj) {
materialProperties.SetOpacity(opacity);
materialProperties.SetCutoff(cutoff);
}
private void OnVariantApplied(Part eventPart, PartVariant variant) {
protected void OnVariantApplied(Part eventPart, PartVariant variant) {
if (_isAttached && eventPart == part.parent) {
UpdateTargets();
}
}
private void OnEditorEvent(ConstructionEventType eventType, Part eventPart) {
protected void OnEditorEvent(ConstructionEventType eventType, Part eventPart) {
if (eventPart != this.part) return;
switch (eventType) {
case ConstructionEventType.PartAttached:
@ -213,7 +210,7 @@ namespace ConformalDecals {
}
}
private void OnAttach() {
protected void OnAttach() {
if (part.parent == null) {
this.LogError("Attach function called but part has no parent!");
_isAttached = false;
@ -224,7 +221,6 @@ namespace ConformalDecals {
this.Log($"Decal attached to {part.parent.partName}");
UpdateTargets();
// hide preview model
decalFrontTransform.gameObject.SetActive(false);
@ -233,11 +229,12 @@ namespace ConformalDecals {
// add to preCull delegate
Camera.onPreCull += Render;
UpdateScale();
//UpdateScale();
UpdateTargets();
UpdateProjection();
}
private void OnDetach() {
protected void OnDetach() {
_isAttached = false;
// unhide preview model
@ -247,13 +244,19 @@ namespace ConformalDecals {
// remove from preCull delegate
Camera.onPreCull -= Render;
UpdateScale();
//UpdateScale();
}
private void UpdateScale() {
protected void UpdateScale() {
var aspectRatio = materialProperties.AspectRatio;
this.Log($"Aspect ratio is {aspectRatio}");
var size = new Vector2(scale, scale * materialProperties.AspectRatio);
// update orthogonal matrix scale
_orthoMatrix = Matrix4x4.identity;
_orthoMatrix[0, 3] = 0.5f;
_orthoMatrix[1, 3] = 0.5f;
_orthoMatrix[0, 0] = 1 / size.x;
_orthoMatrix[1, 1] = 1 / size.y;
_orthoMatrix[2, 2] = 1 / depth;
@ -262,28 +265,67 @@ namespace ConformalDecals {
_decalBounds.center = Vector3.forward * (depth / 2);
_decalBounds.extents = new Vector3(size.x / 2, size.y / 2, depth / 2);
if (decalModelTransform == null) {
this.LogError("decalModelTransform is null!");
}
// rescale preview model
decalModelTransform.localScale = new Vector3(size.x, size.y, (size.x + size.y) / 2);
// update back material scale
if (updateBackScale) {
backMaterial.SetTextureScale(PropertyIDs._MainTex, new Vector2(size.x * _backTextureBaseScale.x, size.y * _backTextureBaseScale.y));
backMaterial.SetTextureScale(PropertyIDs._MainTex, new Vector2(size.x * backTextureBaseScale.x, size.y * backTextureBaseScale.y));
}
// update material scale
materialProperties.SetScale(size);
}
private void UpdateProjection() {
protected void UpdateMaterials() {
materialProperties.UpdateMaterials();
_decalMaterial = materialProperties.DecalMaterial;
_previewMaterial = materialProperties.PreviewMaterial;
// get back material if necessary
if (updateBackScale) {
this.Log("Getting material and base scale for back material");
var backRenderer = decalBackTransform.GetComponent<MeshRenderer>();
if (backRenderer == null) {
this.LogError($"Specified decalBack transform {decalBack} has no renderer attached! Setting updateBackScale to false.");
updateBackScale = false;
}
else {
backMaterial = backRenderer.material;
if (backMaterial == null) {
this.LogError($"Specified decalBack transform {decalBack} has a renderer but no material! Setting updateBackScale to false.");
updateBackScale = false;
}
else {
if (backTextureBaseScale == default) backTextureBaseScale = backMaterial.GetTextureScale(PropertyIDs._MainTex);
}
}
}
if (decalFrontTransform == null) {
this.LogError("No reference to decal front transform");
return;
}
decalFrontTransform.GetComponent<MeshRenderer>().material = _previewMaterial;
}
protected void UpdateProjection() {
if (!_isAttached) return;
var bounds = new OrientedBounds(decalProjectorTransform.localToWorldMatrix, _decalBounds);
// project to each target object
foreach (var target in _targets) {
target.Project(_orthoMatrix, new OrientedBounds(decalProjectorTransform.localToWorldMatrix, _decalBounds), decalProjectorTransform);
target.Project(_orthoMatrix, bounds, decalProjectorTransform, useBaseNormal);
}
}
private void UpdateTargets() {
protected void UpdateTargets() {
if (_targets == null) {
_targets = new List<ProjectionTarget>();
}
@ -319,7 +361,7 @@ namespace ConformalDecals {
}
}
private void UpdateTweakables() {
protected void UpdateTweakables() {
// setup tweakable fields
var scaleField = Fields[nameof(scale)];
var depthField = Fields[nameof(depth)];
@ -369,12 +411,12 @@ namespace ConformalDecals {
}
}
private void Render(Camera camera) {
protected void Render(Camera camera) {
if (!_isAttached) return;
// render on each target object
foreach (var target in _targets) {
target.Render(materialProperties.DecalMaterial, part.mpb, camera);
target.Render(_decalMaterial, part.mpb, camera);
}
}
}

@ -1,4 +1,3 @@
using System;
using ConformalDecals.MaterialModifiers;
using ConformalDecals.Util;
using UnityEngine;
@ -7,29 +6,35 @@ namespace ConformalDecals {
public class ModuleConformalDecalFlag : ModuleConformalDecalBase {
[KSPField] public MaterialTextureProperty flagTextureProperty;
public override void OnLoad(ConfigNode node) {
private const string defaultFlag = "Squad/Flags/default";
if (materialProperties == null) {
// materialProperties is null, so make a new one
materialProperties = ScriptableObject.CreateInstance<MaterialPropertyCollection>();
materialProperties.Initialize();
}
else {
// materialProperties already exists, so make a copy
materialProperties = ScriptableObject.Instantiate(materialProperties);
}
public override void OnLoad(ConfigNode node) {
base.OnLoad(node);
// set shader
materialProperties.SetShader(decalShader);
base.OnLoad(node);
if (HighLogic.LoadedSceneIsGame) {
UpdateMaterials();
UpdateScale();
UpdateProjection();
}
}
public override void OnStart(StartState state) {
base.OnStart(state);
UpdateFlag(EditorLogic.FlagURL != string.Empty ? EditorLogic.FlagURL : HighLogic.CurrentGame.flagURL);
GameEvents.onMissionFlagSelect.Add(UpdateFlag);
if (HighLogic.LoadedSceneIsGame) {
UpdateFlag(EditorLogic.FlagURL != string.Empty ? EditorLogic.FlagURL : HighLogic.CurrentGame.flagURL);
GameEvents.onMissionFlagSelect.Add(UpdateFlag);
}
else {
UpdateFlag(defaultFlag);
}
}
public override void OnIconCreate() {
this.Log("called OnIconCreate");
OnStart(StartState.None);
UpdateScale();
}
private void UpdateFlag(string flagUrl) {
@ -42,14 +47,17 @@ namespace ConformalDecals {
if (flagTextureProperty == null) {
this.Log("Initializing flag property");
flagTextureProperty = new MaterialTextureProperty("_Decal", flagTexture, isMain: true);
flagTextureProperty = ScriptableObject.CreateInstance<MaterialTextureProperty>();
flagTextureProperty.Name = "_Decal";
flagTextureProperty.isMain = true;
materialProperties.AddProperty(flagTextureProperty);
}
else {
flagTextureProperty.texture = flagTexture;
}
else { }
flagTextureProperty.texture = flagTexture;
materialProperties.UpdateMaterials();
UpdateMaterials();
}
}
}

@ -5,35 +5,42 @@ using UnityEngine;
namespace ConformalDecals {
public class ModuleConformalDecalGeneric : ModuleConformalDecalBase {
public override void OnLoad(ConfigNode node) {
if (materialProperties == null) {
// materialProperties is null, so make a new one
materialProperties = ScriptableObject.CreateInstance<MaterialPropertyCollection>();
materialProperties.Initialize();
}
else {
// materialProperties already exists, so make a copy
materialProperties = ScriptableObject.Instantiate(materialProperties);
}
base.OnLoad(node);
// set shader
materialProperties.SetShader(decalShader);
materialProperties.SetShader(shader);
// add texture nodes
foreach (var textureNode in node.GetNodes("TEXTURE")) {
materialProperties.AddProperty(new MaterialTextureProperty(textureNode));
var textureProperty = ScriptableObject.CreateInstance<MaterialTextureProperty>();
textureProperty.ParseNode(textureNode);
materialProperties.AddProperty(textureProperty);
}
// add float nodes
foreach (var floatNode in node.GetNodes("FLOAT")) {
materialProperties.AddProperty(new MaterialFloatProperty(floatNode));
var floatProperty = ScriptableObject.CreateInstance<MaterialFloatProperty>();
floatProperty.ParseNode(floatNode);
materialProperties.AddProperty(floatProperty);
}
// add color nodes
foreach (var colorNode in node.GetNodes("COLOR")) {
materialProperties.AddProperty(new MaterialColorProperty(colorNode));
var colorProperty = ScriptableObject.CreateInstance<MaterialColorProperty>();
colorProperty.ParseNode(colorNode);
materialProperties.AddProperty(colorProperty);
}
base.OnLoad(node);
if (HighLogic.LoadedSceneIsGame) {
UpdateMaterials();
UpdateScale();
UpdateProjection();
}
}
public override void OnIconCreate() {
this.Log("called OnIconCreate");
OnStart(StartState.None);
UpdateScale();
}
}
}

@ -17,36 +17,23 @@ namespace ConformalDecals {
// property block
private readonly MaterialPropertyBlock _decalMPB;
private static readonly int normalID = Shader.PropertyToID("_BumpMap");
private static readonly int normalIDST = Shader.PropertyToID("_BumpMap_ST");
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;
var targetMaterial = targetRenderer.sharedMaterial;
_decalMPB = new MaterialPropertyBlock();
if (useBaseNormal && targetMaterial.HasProperty(normalID)) {
var normal = targetMaterial.GetTexture(normalID);
if (normal != null) {
_decalMPB.SetTexture(normalID, targetMaterial.GetTexture(normalID));
var normalScale = targetMaterial.GetTextureScale(normalID);
var normalOffset = targetMaterial.GetTextureOffset(normalID);
_decalMPB.SetVector(normalIDST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y));
}
}
}
public void Project(Matrix4x4 orthoMatrix, OrientedBounds projectorBounds, Transform projector) {
public void Project(Matrix4x4 orthoMatrix, OrientedBounds projectorBounds, Transform projector, bool useBaseNormal) {
var targetBounds = _targetRenderer.bounds;
if (projectorBounds.Intersects(targetBounds)) {
_projectionEnabled = true;
var targetMaterial = _targetRenderer.sharedMaterial;
var projectorToTargetMatrix = target.worldToLocalMatrix * projector.localToWorldMatrix;
var projectionMatrix = orthoMatrix * projectorToTargetMatrix.inverse;
@ -57,6 +44,19 @@ namespace ConformalDecals {
_decalMPB.SetVector(_decalNormalID, decalNormal);
_decalMPB.SetVector(_decalTangentID, decalTangent);
Debug.Log($"Projection enabled for {target.gameObject}");
if (useBaseNormal && targetMaterial.HasProperty(normalID)) {
var normal = targetMaterial.GetTexture(normalID);
if (normal != null) {
_decalMPB.SetTexture(normalID, targetMaterial.GetTexture(normalID));
var normalScale = targetMaterial.GetTextureScale(normalID);
var normalOffset = targetMaterial.GetTextureOffset(normalID);
_decalMPB.SetVector(normalIDST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y));
}
}
}
else {
_projectionEnabled = false;

Loading…
Cancel
Save