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!)
This commit is contained in:
Andrew Cassidy 2020-06-04 00:12:09 -07:00
parent 00908c9e13
commit 22150574df
No known key found for this signature in database
GPG Key ID: 963017B38FD477A1
17 changed files with 475 additions and 330 deletions

3
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:86aa2ce6f0d2889caae9f3d9ca47a047964ca7d1ed86e30ec6150569bf6455ff oid sha256:9084392fcd8e39a0546b5294d6ffe28119ad869c0421089ccd0ed5d634a034e3
size 30208 size 31232

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:8b6b893cb50203ada71a59ebbc45e85dfcf59fdef7448c3d0bcf8070b30adcf8 oid sha256:2e51f32dadab8cb24dbd6b1c43aefc6648dc7babf261f33c9ed41ee727cdeb97
size 98967 size 125863

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
using System;
using ConformalDecals.MaterialModifiers; using ConformalDecals.MaterialModifiers;
using ConformalDecals.Util; using ConformalDecals.Util;
using UnityEngine; using UnityEngine;
@ -7,29 +6,35 @@ namespace ConformalDecals {
public class ModuleConformalDecalFlag : ModuleConformalDecalBase { public class ModuleConformalDecalFlag : ModuleConformalDecalBase {
[KSPField] public MaterialTextureProperty flagTextureProperty; [KSPField] public MaterialTextureProperty flagTextureProperty;
private const string defaultFlag = "Squad/Flags/default";
public override void OnLoad(ConfigNode node) { 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);
}
// set shader
materialProperties.SetShader(decalShader);
base.OnLoad(node); base.OnLoad(node);
if (HighLogic.LoadedSceneIsGame) {
UpdateMaterials();
UpdateScale();
UpdateProjection();
}
} }
public override void OnStart(StartState state) { public override void OnStart(StartState state) {
base.OnStart(state); base.OnStart(state);
UpdateFlag(EditorLogic.FlagURL != string.Empty ? EditorLogic.FlagURL : HighLogic.CurrentGame.flagURL); if (HighLogic.LoadedSceneIsGame) {
GameEvents.onMissionFlagSelect.Add(UpdateFlag); 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) { private void UpdateFlag(string flagUrl) {
@ -42,14 +47,17 @@ namespace ConformalDecals {
if (flagTextureProperty == null) { if (flagTextureProperty == null) {
this.Log("Initializing flag property"); 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); materialProperties.AddProperty(flagTextureProperty);
} }
else { else { }
flagTextureProperty.texture = flagTexture;
}
materialProperties.UpdateMaterials(); flagTextureProperty.texture = flagTexture;
UpdateMaterials();
} }
} }
} }

View File

@ -5,35 +5,42 @@ using UnityEngine;
namespace ConformalDecals { namespace ConformalDecals {
public class ModuleConformalDecalGeneric : ModuleConformalDecalBase { public class ModuleConformalDecalGeneric : ModuleConformalDecalBase {
public override void OnLoad(ConfigNode node) { public override void OnLoad(ConfigNode node) {
base.OnLoad(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);
}
// set shader // set shader
materialProperties.SetShader(decalShader); materialProperties.SetShader(shader);
// add texture nodes // add texture nodes
foreach (var textureNode in node.GetNodes("TEXTURE")) { 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 // add float nodes
foreach (var floatNode in node.GetNodes("FLOAT")) { 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 // add color nodes
foreach (var colorNode in node.GetNodes("COLOR")) { 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();
} }
} }
} }

View File

@ -17,36 +17,23 @@ namespace ConformalDecals {
// property block // property block
private readonly MaterialPropertyBlock _decalMPB; private readonly MaterialPropertyBlock _decalMPB;
private static readonly int normalID = Shader.PropertyToID("_BumpMap"); private static readonly int normalID = Shader.PropertyToID("_BumpMap");
private static readonly int normalIDST = Shader.PropertyToID("_BumpMap_ST"); private static readonly int normalIDST = Shader.PropertyToID("_BumpMap_ST");
public ProjectionTarget(MeshRenderer targetRenderer, Mesh targetMesh, bool useBaseNormal) { public ProjectionTarget(MeshRenderer targetRenderer, Mesh targetMesh, bool useBaseNormal) {
target = targetRenderer.transform; target = targetRenderer.transform;
_targetRenderer = targetRenderer; _targetRenderer = targetRenderer;
_targetMesh = targetMesh; _targetMesh = targetMesh;
var targetMaterial = targetRenderer.sharedMaterial;
_decalMPB = new MaterialPropertyBlock(); _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; var targetBounds = _targetRenderer.bounds;
if (projectorBounds.Intersects(targetBounds)) { if (projectorBounds.Intersects(targetBounds)) {
_projectionEnabled = true; _projectionEnabled = true;
var targetMaterial = _targetRenderer.sharedMaterial;
var projectorToTargetMatrix = target.worldToLocalMatrix * projector.localToWorldMatrix; var projectorToTargetMatrix = target.worldToLocalMatrix * projector.localToWorldMatrix;
var projectionMatrix = orthoMatrix * projectorToTargetMatrix.inverse; var projectionMatrix = orthoMatrix * projectorToTargetMatrix.inverse;
@ -57,6 +44,19 @@ namespace ConformalDecals {
_decalMPB.SetVector(_decalNormalID, decalNormal); _decalMPB.SetVector(_decalNormalID, decalNormal);
_decalMPB.SetVector(_decalTangentID, decalTangent); _decalMPB.SetVector(_decalTangentID, decalTangent);
Debug.Log($"Projection enabled for {target.gameObject}"); 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 { else {
_projectionEnabled = false; _projectionEnabled = false;