From 22150574df04ca00650ca9c4ff7dea194cef638c Mon Sep 17 00:00:00 2001 From: drewcassidy Date: Thu, 4 Jun 2020 00:12:09 -0700 Subject: [PATCH] Big refactor to enable preview materials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • 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!) --- .gitignore | 3 + Assets/Shaders/DecalFeatureBumped.shader | 52 ++--- Assets/Shaders/DecalPaint.shader | 47 ++--- Assets/Shaders/DecalPaintSpecular.shader | 60 +++--- Assets/Shaders/DecalsCommon.cginc | 82 +++++--- .../ConformalDecals/Parts/decal-blank.cfg | 12 +- .../Plugins/ConformalDecals.dll | 4 +- .../Resources/conformaldecals.shab | 4 +- .../MaterialColorProperty.cs | 14 +- .../MaterialFloatProperty.cs | 14 +- .../MaterialModifiers/MaterialProperty.cs | 34 ++-- .../MaterialPropertyCollection.cs | 60 ++++-- .../MaterialTextureProperty.cs | 108 +++++----- .../ModuleConformalDecalBase.cs | 184 +++++++++++------- .../ModuleConformalDecalFlag.cs | 52 ++--- .../ModuleConformalDecalGeneric.cs | 37 ++-- Source/ConformalDecals/ProjectionTarget.cs | 38 ++-- 17 files changed, 475 insertions(+), 330 deletions(-) diff --git a/.gitignore b/.gitignore index 205a2d3..a8539d2 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Assets/Shaders/DecalFeatureBumped.shader b/Assets/Shaders/DecalFeatureBumped.shader index 8b2a830..bd7dfe7 100644 --- a/Assets/Shaders/DecalFeatureBumped.shader +++ b/Assets/Shaders/DecalFeatureBumped.shader @@ -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; } diff --git a/Assets/Shaders/DecalPaint.shader b/Assets/Shaders/DecalPaint.shader index 09189b9..868e009 100644 --- a/Assets/Shaders/DecalPaint.shader +++ b/Assets/Shaders/DecalPaint.shader @@ -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 diff --git a/Assets/Shaders/DecalPaintSpecular.shader b/Assets/Shaders/DecalPaintSpecular.shader index 0f95716..0e68414 100644 --- a/Assets/Shaders/DecalPaintSpecular.shader +++ b/Assets/Shaders/DecalPaintSpecular.shader @@ -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; } diff --git a/Assets/Shaders/DecalsCommon.cginc b/Assets/Shaders/DecalsCommon.cginc index 6382817..5008743 100644 --- a/Assets/Shaders/DecalsCommon.cginc +++ b/Assets/Shaders/DecalsCommon.cginc @@ -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 - 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.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); diff --git a/Distribution/GameData/ConformalDecals/Parts/decal-blank.cfg b/Distribution/GameData/ConformalDecals/Parts/decal-blank.cfg index 4125aa1..eb33229 100644 --- a/Distribution/GameData/ConformalDecals/Parts/decal-blank.cfg +++ b/Distribution/GameData/ConformalDecals/Parts/decal-blank.cfg @@ -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 } } diff --git a/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll index 558a128..f888974 100644 --- a/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll +++ b/Distribution/GameData/ConformalDecals/Plugins/ConformalDecals.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86aa2ce6f0d2889caae9f3d9ca47a047964ca7d1ed86e30ec6150569bf6455ff -size 30208 +oid sha256:9084392fcd8e39a0546b5294d6ffe28119ad869c0421089ccd0ed5d634a034e3 +size 31232 diff --git a/Distribution/GameData/ConformalDecals/Resources/conformaldecals.shab b/Distribution/GameData/ConformalDecals/Resources/conformaldecals.shab index e758dca..da8cdcb 100644 --- a/Distribution/GameData/ConformalDecals/Resources/conformaldecals.shab +++ b/Distribution/GameData/ConformalDecals/Resources/conformaldecals.shab @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b6b893cb50203ada71a59ebbc45e85dfcf59fdef7448c3d0bcf8070b30adcf8 -size 98967 +oid sha256:2e51f32dadab8cb24dbd6b1c43aefc6648dc7babf261f33c9ed41ee727cdeb97 +size 125863 diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs index 4aba7f0..f60d388 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialColorProperty.cs @@ -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); } } } \ No newline at end of file diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs index a92ab95..1dad8df 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialFloatProperty.cs @@ -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); } } } \ No newline at end of file diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs index a8ea801..629a7e9 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialProperty.cs @@ -1,24 +1,24 @@ using System; using UnityEngine; +using Object = UnityEngine.Object; namespace ConformalDecals.MaterialModifiers { - public abstract class MaterialProperty { - public string PropertyName { get; } + public abstract class MaterialProperty : ScriptableObject { + 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")) { } - - 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); + 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}"); } } } diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs b/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs index 8f80426..0e1322a 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialPropertyCollection.cs @@ -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 _materialProperties; - private List _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; + } + } - [SerializeField] private Shader _decalShader; + public float AspectRatio { + get { + if (MainMaterialTextureProperty == null) return 1; + return MainMaterialTextureProperty.AspectRatio; + } + } + + [SerializeField] private Shader _shader; + [SerializeField] private List _materialProperties; + [SerializeField] private List _textureMaterialProperties; + [SerializeField] private MaterialTextureProperty _mainTexture; private Material _decalMaterial; + private Material _previewMaterial; public void Initialize() { _materialProperties = new List(); @@ -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); } diff --git a/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs b/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs index 03dda7d..3008167 100644 --- a/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs +++ b/Source/ConformalDecals/MaterialModifiers/MaterialTextureProperty.cs @@ -3,29 +3,54 @@ using UnityEngine; namespace ConformalDecals.MaterialModifiers { public class MaterialTextureProperty : MaterialProperty { - public Texture2D texture; + [SerializeField] public Texture2D texture; - public bool IsNormal { get; } - public bool IsMain { get; } - public bool AutoScale { get; } + [SerializeField] public bool isNormal; + [SerializeField] public bool isMain; + [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; - private readonly Vector2 _textureScale; + public Rect TileRect { + get => _tileRect; + set { + _hasTile = !(Mathf.Abs(value.width) < 0.1) || !(Mathf.Abs(value.height) < 0.1); - 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"); + _tileRect = value; + UpdateTiling(); + } + } - 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; } - 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; + } + } } } \ No newline at end of file diff --git a/Source/ConformalDecals/ModuleConformalDecalBase.cs b/Source/ConformalDecals/ModuleConformalDecalBase.cs index e3715ad..73bb99a 100644 --- a/Source/ConformalDecals/ModuleConformalDecalBase.cs +++ b/Source/ConformalDecals/ModuleConformalDecalBase.cs @@ -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 _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(); - 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(); + 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; + // clone materialProperties and setup queue + if (HighLogic.LoadedSceneIsGame) { + materialProperties = ScriptableObject.Instantiate(materialProperties); + materialProperties.SetRenderQueue(DecalQueue); - // instantiate decal material and set uniqueish queue - materialProperties.SetRenderQueue(DecalQueue); + // set initial attachment state + if (part.parent == null) { + _isAttached = false; + } + else { + OnAttach(); + } + } - // set initial attachment state - if (part.parent == null) { - OnDetach(); - } - 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(); + 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().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(); } @@ -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); } } } diff --git a/Source/ConformalDecals/ModuleConformalDecalFlag.cs b/Source/ConformalDecals/ModuleConformalDecalFlag.cs index 1585c4e..1c53cef 100644 --- a/Source/ConformalDecals/ModuleConformalDecalFlag.cs +++ b/Source/ConformalDecals/ModuleConformalDecalFlag.cs @@ -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; + private const string defaultFlag = "Squad/Flags/default"; + public override void OnLoad(ConfigNode node) { - - if (materialProperties == null) { - // materialProperties is null, so make a new one - materialProperties = ScriptableObject.CreateInstance(); - materialProperties.Initialize(); - } - else { - // materialProperties already exists, so make a copy - materialProperties = ScriptableObject.Instantiate(materialProperties); - } - - // 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(); + flagTextureProperty.Name = "_Decal"; + flagTextureProperty.isMain = true; materialProperties.AddProperty(flagTextureProperty); } - else { - flagTextureProperty.texture = flagTexture; - } + else { } - materialProperties.UpdateMaterials(); + flagTextureProperty.texture = flagTexture; + + + UpdateMaterials(); } } } \ No newline at end of file diff --git a/Source/ConformalDecals/ModuleConformalDecalGeneric.cs b/Source/ConformalDecals/ModuleConformalDecalGeneric.cs index 7a2fa76..1025710 100644 --- a/Source/ConformalDecals/ModuleConformalDecalGeneric.cs +++ b/Source/ConformalDecals/ModuleConformalDecalGeneric.cs @@ -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(); - 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(); + 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(); + 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(); + 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(); } } } \ No newline at end of file diff --git a/Source/ConformalDecals/ProjectionTarget.cs b/Source/ConformalDecals/ProjectionTarget.cs index ce69143..9d559b6 100644 --- a/Source/ConformalDecals/ProjectionTarget.cs +++ b/Source/ConformalDecals/ProjectionTarget.cs @@ -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;