From 914100b0a427b3c9f892017344bddf01b12bef12 Mon Sep 17 00:00:00 2001 From: laurids Date: Sun, 3 Oct 2021 18:30:32 +0200 Subject: [PATCH] True Shadow --- .../Prefabs/UI/PlanetIndicator.prefab | 68 ++- .../UI/PlanetIndicatorDistrictSymbol.prefab | 52 +- Assets/Le Tai's Asset.meta | 8 + Assets/Le Tai's Asset/TrueShadow.meta | 8 + .../TrueShadow/LeTai.TrueShadow.asmdef | 13 + .../TrueShadow/LeTai.TrueShadow.asmdef.meta | 7 + .../TrueShadow/Offline Documentation.pdf | Bin 0 -> 69015 bytes .../TrueShadow/Offline Documentation.pdf.meta | 7 + .../Online Documentation (Recommended).txt | 1 + ...nline Documentation (Recommended).txt.meta | 7 + .../Le Tai's Asset/TrueShadow/Resources.meta | 8 + .../TrueShadow/Resources/Shaders.meta | 8 + .../Resources/Shaders/EfficientBlur.shader | 103 ++++ .../Shaders/EfficientBlur.shader.meta | 9 + .../Shaders/ImprintPostProcess.shader | 61 ++ .../Shaders/ImprintPostProcess.shader.meta | 3 + .../Resources/Shaders/ShadowCutout.shader | 59 ++ .../Shaders/ShadowCutout.shader.meta | 9 + .../Shaders/ShadowPostProcess.shader | 69 +++ .../Shaders/ShadowPostProcess.shader.meta | 3 + .../Shaders/TrueShadow-Additive.shader | 54 ++ .../Shaders/TrueShadow-Additive.shader.meta | 3 + .../Shaders/TrueShadow-Multiply.shader | 56 ++ .../Shaders/TrueShadow-Multiply.shader.meta | 3 + .../Shaders/TrueShadow-Normal.shader | 54 ++ .../Shaders/TrueShadow-Normal.shader.meta | 3 + .../Shaders/TrueShadow-Screen.shader | 54 ++ .../Shaders/TrueShadow-Screen.shader.meta | 3 + .../Resources/Shaders/TrueShadow.cginc | 62 ++ .../Resources/Shaders/TrueShadow.cginc.meta | 3 + Assets/Le Tai's Asset/TrueShadow/Scripts.meta | 8 + .../TrueShadow/Scripts/DebugSettings.cs | 78 +++ .../TrueShadow/Scripts/DebugSettings.cs.meta | 11 + .../TrueShadow/Scripts/Editor.meta | 8 + .../Editor/AutoCustomScriptingDefine.cs | 33 + .../Editor/AutoCustomScriptingDefine.cs.meta | 3 + .../Scripts/Editor/EditorProperty.cs | 59 ++ .../Scripts/Editor/EditorProperty.cs.meta | 3 + .../Scripts/Editor/InlineToolbar.cs | 67 +++ .../Scripts/Editor/InlineToolbar.cs.meta | 3 + .../Scripts/Editor/KnobPropertyDrawer.cs | 184 ++++++ .../Scripts/Editor/KnobPropertyDrawer.cs.meta | 3 + .../Editor/LeTai.TrueShadow.Editor.asmdef | 16 + .../LeTai.TrueShadow.Editor.asmdef.meta | 7 + .../Scripts/Editor/MigrateToV0_5Window.cs | 104 ++++ .../Editor/MigrateToV0_5Window.cs.meta | 3 + .../Scripts/Editor/PrefabEventHandler.cs | 19 + .../Scripts/Editor/PrefabEventHandler.cs.meta | 3 + .../Scripts/Editor/ScenceGizmoAutoDisable.cs | 62 ++ .../Editor/ScenceGizmoAutoDisable.cs.meta | 11 + .../Scripts/Editor/SpreadSliderDrawer.cs | 87 +++ .../Scripts/Editor/SpreadSliderDrawer.cs.meta | 3 + .../TrueShadow/Scripts/Editor/Textures.meta | 8 + .../Scripts/Editor/Textures/Inner Shadow.png | Bin 0 -> 459 bytes .../Editor/Textures/Inner Shadow.png.meta | 130 ++++ .../Scripts/Editor/Textures/Knob_BG.png | Bin 0 -> 1090 bytes .../Scripts/Editor/Textures/Knob_BG.png.meta | 130 ++++ .../Scripts/Editor/Textures/Knob_FG.png | Bin 0 -> 1235 bytes .../Scripts/Editor/Textures/Knob_FG.png.meta | 130 ++++ .../Scripts/Editor/Textures/Outer Shadow.png | Bin 0 -> 724 bytes .../Editor/Textures/Outer Shadow.png.meta | 130 ++++ .../Editor/Textures/True Shadow Icon.png | Bin 0 -> 2091 bytes .../Editor/Textures/True Shadow Icon.png.meta | 128 ++++ .../Scripts/Editor/TrueShadowEditor.cs | 104 ++++ .../Scripts/Editor/TrueShadowEditor.cs.meta | 3 + .../TrueShadow/Scripts/Editor/Utility.cs | 22 + .../TrueShadow/Scripts/Editor/Utility.cs.meta | 3 + .../TrueShadow/Scripts/Effects.meta | 8 + .../TrueShadow/Scripts/Effects/BlurConfig.cs | 9 + .../Scripts/Effects/BlurConfig.cs.meta | 11 + .../Scripts/Effects/IBlurAlgorithm.cs | 15 + .../Scripts/Effects/IBlurAlgorithm.cs.meta | 11 + .../Scripts/Effects/ScalableBlur.cs | 104 ++++ .../Scripts/Effects/ScalableBlur.cs.meta | 11 + .../Scripts/Effects/ScalableBlurConfig.cs | 110 ++++ .../Effects/ScalableBlurConfig.cs.meta | 11 + .../Scripts/Effects/ShaderProperties.cs | 37 ++ .../Scripts/Effects/ShaderProperties.cs.meta | 11 + .../TrueShadow/Scripts/Helper.meta | 3 + .../Scripts/Helper/AnimatedBiStateButton.cs | 138 +++++ .../Helper/AnimatedBiStateButton.cs.meta | 3 + .../TrueShadow/Scripts/Helper/InsetOnPress.cs | 69 +++ .../Scripts/Helper/InsetOnPress.cs.meta | 3 + .../Scripts/Helper/InteractiveShadow.cs | 242 ++++++++ .../Scripts/Helper/InteractiveShadow.cs.meta | 3 + .../TrueShadow/Scripts/PluginInterfaces.cs | 40 ++ .../Scripts/PluginInterfaces.cs.meta | 11 + .../TrueShadow/Scripts/ShadowFactory.cs | 312 ++++++++++ .../TrueShadow/Scripts/ShadowFactory.cs.meta | 11 + .../Scripts/ShadowRenderer.MaskHandling.cs | 76 +++ .../ShadowRenderer.MaskHandling.cs.meta | 3 + .../TrueShadow/Scripts/ShadowRenderer.cs | 189 ++++++ .../TrueShadow/Scripts/ShadowRenderer.cs.meta | 3 + .../Scripts/ShadowSettingSnapshot.cs | 139 +++++ .../Scripts/ShadowSettingSnapshot.cs.meta | 11 + .../TrueShadow/Scripts/ShadowSorter.cs | 193 ++++++ .../TrueShadow/Scripts/ShadowSorter.cs.meta | 11 + .../TrueShadow/Scripts/Structure.meta | 8 + .../TrueShadow/Scripts/Structure/BlendMode.cs | 42 ++ .../Scripts/Structure/BlendMode.cs.meta | 3 + .../Scripts/Structure/ColorBleedMode.cs | 11 + .../Scripts/Structure/ColorBleedMode.cs.meta | 3 + .../Scripts/Structure/ShadowContainer.cs | 29 + .../Scripts/Structure/ShadowContainer.cs.meta | 3 + .../Scripts/TrueShadow.Invalidator.cs | 208 +++++++ .../Scripts/TrueShadow.Invalidator.cs.meta | 3 + .../TrueShadow/Scripts/TrueShadow.Plugins.cs | 109 ++++ .../Scripts/TrueShadow.Plugins.cs.meta | 3 + .../TrueShadow/Scripts/TrueShadow.cs | 565 ++++++++++++++++++ .../TrueShadow/Scripts/TrueShadow.cs.meta | 11 + .../TrueShadow/Scripts/Utilities.meta | 8 + .../Scripts/Utilities/ExtensionMethods.cs | 134 +++++ .../Utilities/ExtensionMethods.cs.meta | 11 + .../TrueShadow/Scripts/Utilities/HashCode.cs | 85 +++ .../Scripts/Utilities/HashCode.cs.meta | 3 + .../Scripts/Utilities/IndexedSet.cs | 138 +++++ .../Scripts/Utilities/IndexedSet.cs.meta | 3 + .../TrueShadow/Scripts/Utilities/Math.cs | 51 ++ .../TrueShadow/Scripts/Utilities/Math.cs.meta | 11 + .../Utilities/PropertyDrawerAttributes.cs | 10 + .../PropertyDrawerAttributes.cs.meta | 3 + .../TrueShadow/Scripts/Utilities/ShaderID.cs | 18 + .../Scripts/Utilities/ShaderID.cs.meta | 3 + .../Utilities/SpreadSliderAttribute.cs | 9 + .../Utilities/SpreadSliderAttribute.cs.meta | 3 + .../TrueShadow/Scripts/Utilities/Utility.cs | 52 ++ .../Scripts/Utilities/Utility.cs.meta | 11 + .../Le Tai's Asset/TrueShadow/changelog.txt | 78 +++ .../TrueShadow/changelog.txt.meta | 7 + ProjectSettings/ProjectSettings.asset | 2 +- 130 files changed, 5769 insertions(+), 28 deletions(-) create mode 100644 Assets/Le Tai's Asset.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/LeTai.TrueShadow.asmdef create mode 100644 Assets/Le Tai's Asset/TrueShadow/LeTai.TrueShadow.asmdef.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Offline Documentation.pdf create mode 100644 Assets/Le Tai's Asset/TrueShadow/Offline Documentation.pdf.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Online Documentation (Recommended).txt create mode 100644 Assets/Le Tai's Asset/TrueShadow/Online Documentation (Recommended).txt.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/EfficientBlur.shader create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/EfficientBlur.shader.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ImprintPostProcess.shader create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ImprintPostProcess.shader.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowCutout.shader create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowCutout.shader.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowPostProcess.shader create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowPostProcess.shader.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Additive.shader create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Additive.shader.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Multiply.shader create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Multiply.shader.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Normal.shader create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Normal.shader.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Screen.shader create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Screen.shader.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow.cginc create mode 100644 Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow.cginc.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/DebugSettings.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/DebugSettings.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/AutoCustomScriptingDefine.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/AutoCustomScriptingDefine.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/EditorProperty.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/EditorProperty.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/InlineToolbar.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/InlineToolbar.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/KnobPropertyDrawer.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/KnobPropertyDrawer.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/LeTai.TrueShadow.Editor.asmdef create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/LeTai.TrueShadow.Editor.asmdef.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/MigrateToV0_5Window.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/MigrateToV0_5Window.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/PrefabEventHandler.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/PrefabEventHandler.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/ScenceGizmoAutoDisable.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/ScenceGizmoAutoDisable.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/SpreadSliderDrawer.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/SpreadSliderDrawer.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Inner Shadow.png create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Inner Shadow.png.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_BG.png create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_BG.png.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_FG.png create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_FG.png.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Outer Shadow.png create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Outer Shadow.png.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/True Shadow Icon.png create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/True Shadow Icon.png.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/TrueShadowEditor.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/TrueShadowEditor.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Utility.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Utility.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/BlurConfig.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/BlurConfig.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/IBlurAlgorithm.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/IBlurAlgorithm.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlur.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlur.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlurConfig.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlurConfig.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ShaderProperties.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ShaderProperties.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Helper.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/AnimatedBiStateButton.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/AnimatedBiStateButton.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InsetOnPress.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InsetOnPress.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InteractiveShadow.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InteractiveShadow.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/PluginInterfaces.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/PluginInterfaces.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowFactory.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowFactory.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.MaskHandling.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.MaskHandling.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSettingSnapshot.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSettingSnapshot.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSorter.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSorter.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Structure.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/BlendMode.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/BlendMode.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ColorBleedMode.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ColorBleedMode.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ShadowContainer.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ShadowContainer.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Invalidator.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Invalidator.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Plugins.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Plugins.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ExtensionMethods.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ExtensionMethods.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/HashCode.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/HashCode.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/IndexedSet.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/IndexedSet.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Math.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Math.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/PropertyDrawerAttributes.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/PropertyDrawerAttributes.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ShaderID.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ShaderID.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/SpreadSliderAttribute.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/SpreadSliderAttribute.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Utility.cs create mode 100644 Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Utility.cs.meta create mode 100644 Assets/Le Tai's Asset/TrueShadow/changelog.txt create mode 100644 Assets/Le Tai's Asset/TrueShadow/changelog.txt.meta diff --git a/Assets/GWConquest/Prefabs/UI/PlanetIndicator.prefab b/Assets/GWConquest/Prefabs/UI/PlanetIndicator.prefab index 714b9da..70ce8e5 100644 --- a/Assets/GWConquest/Prefabs/UI/PlanetIndicator.prefab +++ b/Assets/GWConquest/Prefabs/UI/PlanetIndicator.prefab @@ -60,6 +60,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 0.8, g: 0.25490198, b: 0.14509805, a: 0.62352943} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -85,7 +86,7 @@ GameObject: - component: {fileID: 2845208572781105551} - component: {fileID: 6883330568249547342} - component: {fileID: 7197385088179281400} - - component: {fileID: 5556159197701511021} + - component: {fileID: 3216987783940624451} m_Layer: 5 m_Name: FactionIcon m_TagString: Untagged @@ -135,6 +136,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -149,7 +151,7 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 ---- !u!114 &5556159197701511021 +--- !u!114 &3216987783940624451 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -158,12 +160,25 @@ MonoBehaviour: m_GameObject: {fileID: 1004975018085727846} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} + m_Script: {fileID: 11500000, guid: 52c162dc854d2f24fa639ba0623de5ef, type: 3} m_Name: m_EditorClassIdentifier: - m_EffectColor: {r: 0, g: 0, b: 0, a: 0.6431373} - m_EffectDistance: {x: 1, y: -1} - m_UseGraphicAlpha: 1 + size: 3.48 + spread: 0.916 + offsetAngle: 90 + offsetDistance: 8 + offset: {x: 0, y: 0} + color: {r: 0, g: 0, b: 0, a: 1} + inset: 0 + blendMode: 0 + useCasterAlpha: 1 + ignoreCasterColor: 0 + colorBleedMode: 0 + shadowAsSibling: 0 + cutout: 0 + baked: 0 + modifiedFromInspector: 0 + bakedShadows: [] --- !u!1 &1425561383975781025 GameObject: m_ObjectHideFlags: 0 @@ -224,6 +239,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 0.8, g: 0.25490198, b: 0.14509805, a: 0.62352943} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -298,6 +314,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -348,7 +365,7 @@ RectTransform: - {fileID: 1577843074573127529} - {fileID: 5933915466098524944} m_Father: {fileID: 7384831771431062766} - m_RootOrder: 1 + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -526,6 +543,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 0.8901961, b: 0.5686275, a: 0.62352943} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -600,6 +618,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 0.681, g: 0.681, b: 0.681, a: 0.99607843} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -674,6 +693,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 0.681, g: 0.681, b: 0.681, a: 0.99607843} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -699,7 +719,7 @@ GameObject: - component: {fileID: 7493439069602050584} - component: {fileID: 8817467615109816757} - component: {fileID: 8517481495962601137} - - component: {fileID: 8442851035215243681} + - component: {fileID: 5092933628985013823} m_Layer: 5 m_Name: Text m_TagString: Untagged @@ -719,7 +739,7 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 7384831771431062766} - m_RootOrder: 0 + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -749,6 +769,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 0.688, g: 0.688, b: 0.688, a: 1} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -767,7 +788,7 @@ MonoBehaviour: m_VerticalOverflow: 0 m_LineSpacing: 1 m_Text: MENAXURIE ---- !u!114 &8442851035215243681 +--- !u!114 &5092933628985013823 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -776,12 +797,25 @@ MonoBehaviour: m_GameObject: {fileID: 3536075192841412823} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} + m_Script: {fileID: 11500000, guid: 52c162dc854d2f24fa639ba0623de5ef, type: 3} m_Name: m_EditorClassIdentifier: - m_EffectColor: {r: 0, g: 0, b: 0, a: 0.63529414} - m_EffectDistance: {x: 2, y: -2} - m_UseGraphicAlpha: 1 + size: 3.48 + spread: 0.916 + offsetAngle: 90 + offsetDistance: 8 + offset: {x: 0, y: 0} + color: {r: 0, g: 0, b: 0, a: 1} + inset: 0 + blendMode: 0 + useCasterAlpha: 1 + ignoreCasterColor: 0 + colorBleedMode: 0 + shadowAsSibling: 0 + cutout: 0 + baked: 0 + modifiedFromInspector: 0 + bakedShadows: [] --- !u!1 &4082808148708746503 GameObject: m_ObjectHideFlags: 0 @@ -842,6 +876,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 0.681, g: 0.681, b: 0.681, a: 0.99607843} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -916,6 +951,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 0.8901961, b: 0.5686275, a: 0.62352943} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -1027,6 +1063,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 0.681, g: 0.681, b: 0.681, a: 0.99607843} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -1146,8 +1183,8 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - - {fileID: 7493439069602050584} - {fileID: 3206999086042524838} + - {fileID: 7493439069602050584} - {fileID: 6852626612145593344} m_Father: {fileID: 0} m_RootOrder: 0 @@ -1314,6 +1351,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 0.64446914, b: 0.2688679, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: diff --git a/Assets/GWConquest/Prefabs/UI/PlanetIndicatorDistrictSymbol.prefab b/Assets/GWConquest/Prefabs/UI/PlanetIndicatorDistrictSymbol.prefab index 8c17924..3d135d6 100644 --- a/Assets/GWConquest/Prefabs/UI/PlanetIndicatorDistrictSymbol.prefab +++ b/Assets/GWConquest/Prefabs/UI/PlanetIndicatorDistrictSymbol.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 7541130419680964749} - component: {fileID: 7541130419680964747} - component: {fileID: 7541130419680964746} - - component: {fileID: 6281905439376641608} + - component: {fileID: 5485887525763253888} m_Layer: 5 m_Name: PlanetIndicatorDistrictSymbol m_TagString: Untagged @@ -62,6 +62,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -76,7 +77,7 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 ---- !u!114 &6281905439376641608 +--- !u!114 &5485887525763253888 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -85,12 +86,25 @@ MonoBehaviour: m_GameObject: {fileID: 7541130419680964748} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} + m_Script: {fileID: 11500000, guid: 52c162dc854d2f24fa639ba0623de5ef, type: 3} m_Name: m_EditorClassIdentifier: - m_EffectColor: {r: 0, g: 0, b: 0, a: 0.63529414} - m_EffectDistance: {x: 1, y: -1} - m_UseGraphicAlpha: 1 + size: 3.48 + spread: 0.916 + offsetAngle: 90 + offsetDistance: 8 + offset: {x: 0, y: 0} + color: {r: 0, g: 0, b: 0, a: 1} + inset: 0 + blendMode: 0 + useCasterAlpha: 1 + ignoreCasterColor: 0 + colorBleedMode: 0 + shadowAsSibling: 0 + cutout: 0 + baked: 0 + modifiedFromInspector: 0 + bakedShadows: [] --- !u!1 &8644822413282706300 GameObject: m_ObjectHideFlags: 0 @@ -102,7 +116,7 @@ GameObject: - component: {fileID: 5640206779338511740} - component: {fileID: 8489971881928985428} - component: {fileID: 2380150125148524420} - - component: {fileID: 7035351003660383945} + - component: {fileID: 1054513047492580219} m_Layer: 5 m_Name: Image m_TagString: Untagged @@ -152,6 +166,7 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: @@ -166,7 +181,7 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 ---- !u!114 &7035351003660383945 +--- !u!114 &1054513047492580219 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -175,9 +190,22 @@ MonoBehaviour: m_GameObject: {fileID: 8644822413282706300} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} + m_Script: {fileID: 11500000, guid: 52c162dc854d2f24fa639ba0623de5ef, type: 3} m_Name: m_EditorClassIdentifier: - m_EffectColor: {r: 0, g: 0, b: 0, a: 0.63529414} - m_EffectDistance: {x: 0.5, y: -0.5} - m_UseGraphicAlpha: 1 + size: 3.48 + spread: 0.916 + offsetAngle: 90 + offsetDistance: 8 + offset: {x: 0, y: 0} + color: {r: 0, g: 0, b: 0, a: 1} + inset: 0 + blendMode: 0 + useCasterAlpha: 1 + ignoreCasterColor: 0 + colorBleedMode: 0 + shadowAsSibling: 0 + cutout: 0 + baked: 0 + modifiedFromInspector: 0 + bakedShadows: [] diff --git a/Assets/Le Tai's Asset.meta b/Assets/Le Tai's Asset.meta new file mode 100644 index 0000000..be8723d --- /dev/null +++ b/Assets/Le Tai's Asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e65753410138a4801a6caf79d625ccd1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow.meta b/Assets/Le Tai's Asset/TrueShadow.meta new file mode 100644 index 0000000..5d9e00a --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 345755c04661c49208b308ceef47c557 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/LeTai.TrueShadow.asmdef b/Assets/Le Tai's Asset/TrueShadow/LeTai.TrueShadow.asmdef new file mode 100644 index 0000000..644d226 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/LeTai.TrueShadow.asmdef @@ -0,0 +1,13 @@ +{ + "name": "LeTai.TrueShadow", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/LeTai.TrueShadow.asmdef.meta b/Assets/Le Tai's Asset/TrueShadow/LeTai.TrueShadow.asmdef.meta new file mode 100644 index 0000000..ac00e7c --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/LeTai.TrueShadow.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7ab3663edede26740845931880bf22af +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Offline Documentation.pdf b/Assets/Le Tai's Asset/TrueShadow/Offline Documentation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..260358661e5e0a03c02511c368810fd1236071f2 GIT binary patch literal 69015 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 T|2`m)Fer$OgF{MDn(V&-zp_HA literal 0 HcmV?d00001 diff --git a/Assets/Le Tai's Asset/TrueShadow/Offline Documentation.pdf.meta b/Assets/Le Tai's Asset/TrueShadow/Offline Documentation.pdf.meta new file mode 100644 index 0000000..f375de1 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Offline Documentation.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c4e2187761ed8294780562fc58e8205e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Online Documentation (Recommended).txt b/Assets/Le Tai's Asset/TrueShadow/Online Documentation (Recommended).txt new file mode 100644 index 0000000..3ce6617 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Online Documentation (Recommended).txt @@ -0,0 +1 @@ +https://leloctai.com/trueshadow/docs/ \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Online Documentation (Recommended).txt.meta b/Assets/Le Tai's Asset/TrueShadow/Online Documentation (Recommended).txt.meta new file mode 100644 index 0000000..66540bc --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Online Documentation (Recommended).txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 63a70f84fa489504abf9f477bd12d05e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources.meta b/Assets/Le Tai's Asset/TrueShadow/Resources.meta new file mode 100644 index 0000000..2afc3fa --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d5929e1d292a9d84180571372bbb84f6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders.meta new file mode 100644 index 0000000..5a9e700 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 316e6d240db2e974d97ea072bee96163 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/EfficientBlur.shader b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/EfficientBlur.shader new file mode 100644 index 0000000..45c96d7 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/EfficientBlur.shader @@ -0,0 +1,103 @@ +Shader "Hidden/EfficientBlur" +{ + Properties + { + _MainTex ("Texture", 2D) = "white" {} + } + + CGINCLUDE + #include "UnityCG.cginc" + + sampler2D _MainTex; + half4 _MainTex_TexelSize; + half4 _MainTex_ST; + + uniform half _Radius; + + struct v2f + { + half4 vertex : SV_POSITION; + half4 texcoord : TEXCOORD0; + }; + + /*struct appdata + { + half4 vertex : POSITION; + half2 texcoord: TEXCOORD0; + }*/ + + v2f vert(appdata_img v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + + half4 offset = half2(-0.5h, 0.5h).xxyy; //-x, -y, x, y + offset *= _MainTex_TexelSize.xyxy; + offset *= _Radius; + o.texcoord = v.texcoord.xyxy + offset; + + return o; + } + + half4 frag(v2f i) : SV_Target + { +// half4 o = +// tex2D(_MainTex, i.texcoord.xw); +// o += tex2D(_MainTex, i.texcoord.zw); +// o += tex2D(_MainTex, i.texcoord.xy); +// o += tex2D(_MainTex, i.texcoord.zy); +// o /= 4.0; + + + //Pray to the compiler god these will MAD + half4 o = + tex2D(_MainTex, i.texcoord.xw) / 4.0h; + o += tex2D(_MainTex, i.texcoord.zw) / 4.0h; + o += tex2D(_MainTex, i.texcoord.xy) / 4.0h; + o += tex2D(_MainTex, i.texcoord.zy) / 4.0h; + + return o; + } + ENDCG + + SubShader + { + Cull Off ZWrite Off ZTest Always Blend Off + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + ENDCG + } + + Pass + { + CGPROGRAM + //Crop before blur + #pragma vertex vertCrop + #pragma fragment frag + + half4 _CropRegion; + + half2 getNewUV(half2 oldUV) + { + return lerp(_CropRegion.xy, _CropRegion.zw, oldUV); + } + + v2f vertCrop(appdata_img v) + { + v2f o = vert(v); + + o.texcoord.xy = getNewUV(o.texcoord.xy); + o.texcoord.zw = getNewUV(o.texcoord.zw); + + return o; + } + ENDCG + } + } + + FallBack Off +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/EfficientBlur.shader.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/EfficientBlur.shader.meta new file mode 100644 index 0000000..c42e372 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/EfficientBlur.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 11ecd49e831fe6c4f8f68b583206390c +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ImprintPostProcess.shader b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ImprintPostProcess.shader new file mode 100644 index 0000000..4248d0f --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ImprintPostProcess.shader @@ -0,0 +1,61 @@ +Shader "Hidden/TrueShadow/ImprintPostProcess" +{ + Properties + { + _MainTex ("Texture", 2D) = "white" {} + } + SubShader + { + Cull Off ZWrite Off ZTest Always + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_local __ BLEACH + #pragma multi_compile_local __ INSET + + #include "UnityCG.cginc" + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f + { + float2 uv : TEXCOORD0; + float4 vertex : SV_POSITION; + }; + + v2f vert(appdata v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + return o; + } + + sampler2D _MainTex; + + half4 frag(v2f i) : SV_Target + { + #if BLEACH + half4 color = half4(1,1,1, tex2D(_MainTex, i.uv).a); + #else + half4 color = tex2D(_MainTex, i.uv); + #endif + + #if INSET + color.a = 1 - color.a; + #endif + + color.rgb*=color.a; + return color; + } + ENDCG + } + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ImprintPostProcess.shader.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ImprintPostProcess.shader.meta new file mode 100644 index 0000000..647f43d --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ImprintPostProcess.shader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 56c6b737089145c590ee9874dc3c09f7 +timeCreated: 1602477197 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowCutout.shader b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowCutout.shader new file mode 100644 index 0000000..c0d32be --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowCutout.shader @@ -0,0 +1,59 @@ +Shader "Hidden/TrueShadow/Cutout" +{ + Properties + { + _MainTex ("Texture", 2D) = "white" {} + _Offset ("Offset", Vector) = (0,0,0,0) + } + SubShader + { + Cull Off ZWrite Off ZTest Always + Blend Zero OneMinusSrcAlpha, Zero OneMinusSrcAlpha + BlendOp Add, Add +// ColorMask a + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f + { + float2 uv : TEXCOORD0; + float4 vertex : SV_POSITION; + }; + + v2f vert(appdata v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + return o; + } + + uniform sampler2D _MainTex; + uniform float2 _Offset; + uniform float _OverflowAlpha; + + fixed4 frag(v2f i) : SV_Target + { + float2 uv = i.uv + _Offset; + + if (any(uv > 1) || any(uv < 0)) + return _OverflowAlpha; + + return fixed4(0, 0, 0, tex2D(_MainTex, uv).a); + } + ENDCG + } + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowCutout.shader.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowCutout.shader.meta new file mode 100644 index 0000000..f814c0c --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowCutout.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 77361e944e5710f4993a94af0720a22f +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowPostProcess.shader b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowPostProcess.shader new file mode 100644 index 0000000..bf5116b --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowPostProcess.shader @@ -0,0 +1,69 @@ +Shader "Hidden/TrueShadow/PostProcess" +{ + Properties + { + _MainTex ("Texture", 2D) = "white" {} + _Offset ("Offset", Vector) = (0,0,0,0) + } + SubShader + { + Cull Off ZWrite Off ZTest Always + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f + { + float2 uv : TEXCOORD0; + float4 vertex : SV_POSITION; + }; + + v2f vert(appdata v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + return o; + } + + uniform sampler2D _ShadowTex; + uniform sampler2D _MainTex; + uniform float2 _Offset; + uniform float _OverflowAlpha; + uniform float _AlphaMultiplier; + + fixed4 frag(v2f i) : SV_Target + { + half4 shadow = tex2D(_ShadowTex, i.uv); + + half alpha = shadow.a; + half alphaBoosted = saturate(alpha * _AlphaMultiplier); + shadow.a = alphaBoosted; + shadow.rgb = shadow.rgb / alpha * alphaBoosted; + + float cutOut; + float2 cutoutUv = i.uv + _Offset; + if (any(cutoutUv > 1) || any(cutoutUv < 0)) + cutOut = _OverflowAlpha; + else + cutOut = tex2D(_MainTex, cutoutUv).a; + + shadow *= 1 - cutOut; + + return shadow; + } + ENDCG + } + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowPostProcess.shader.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowPostProcess.shader.meta new file mode 100644 index 0000000..3028b46 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/ShadowPostProcess.shader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 64f3fdefd5e7411dae947574155e2253 +timeCreated: 1614411795 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Additive.shader b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Additive.shader new file mode 100644 index 0000000..6c738af --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Additive.shader @@ -0,0 +1,54 @@ +Shader "UI/TrueShadow-Additive" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + + _StencilComp ("Stencil Comparison", Float) = 8 + _Stencil ("Stencil ID", Float) = 0 + _StencilOp ("Stencil Operation", Float) = 0 + _StencilWriteMask ("Stencil Write Mask", Float) = 255 + _StencilReadMask ("Stencil Read Mask", Float) = 255 + + _ColorMask ("Color Mask", Float) = 15 + + [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 + } + + SubShader + { + Tags + { + "Queue"="Transparent" + "IgnoreProjector"="True" + "RenderType"="Transparent" + "PreviewType"="Plane" + "CanUseSpriteAtlas"="True" + } + + Stencil + { + Ref [_Stencil] + Comp [_StencilComp] + Pass [_StencilOp] + ReadMask [_StencilReadMask] + WriteMask [_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest [unity_GUIZTestMode] + Blend One One + ColorMask [_ColorMask] + + Pass + { + CGPROGRAM + #include "TrueShadow.cginc" + #pragma vertex vert + #pragma fragment frag + ENDCG + } + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Additive.shader.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Additive.shader.meta new file mode 100644 index 0000000..fec4ade --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Additive.shader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6c0e0b60ad64412c8cfcffd1553dcc06 +timeCreated: 1594096623 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Multiply.shader b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Multiply.shader new file mode 100644 index 0000000..9005267 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Multiply.shader @@ -0,0 +1,56 @@ +Shader "UI/TrueShadow-Multiply" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + + _StencilComp ("Stencil Comparison", Float) = 8 + _Stencil ("Stencil ID", Float) = 0 + _StencilOp ("Stencil Operation", Float) = 0 + _StencilWriteMask ("Stencil Write Mask", Float) = 255 + _StencilReadMask ("Stencil Read Mask", Float) = 255 + + _ColorMask ("Color Mask", Float) = 15 + + [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 + } + + SubShader + { + Tags + { + "Queue"="Transparent" + "IgnoreProjector"="True" + "RenderType"="Transparent" + "PreviewType"="Plane" + "CanUseSpriteAtlas"="True" + } + + Stencil + { + Ref [_Stencil] + Comp [_StencilComp] + Pass [_StencilOp] + ReadMask [_StencilReadMask] + WriteMask [_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest [unity_GUIZTestMode] + Blend DstColor Zero + ColorMask [_ColorMask] + + Pass + { + CGPROGRAM + #define DO_BLEND_POSTPROCESS color.rgb = 1 - color.a + color.rgb; + + #include "TrueShadow.cginc" + #pragma vertex vert + #pragma fragment frag + ENDCG + } + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Multiply.shader.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Multiply.shader.meta new file mode 100644 index 0000000..681b96f --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Multiply.shader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 84318cb7c8604c59a985a677d93e4048 +timeCreated: 1594096631 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Normal.shader b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Normal.shader new file mode 100644 index 0000000..757339d --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Normal.shader @@ -0,0 +1,54 @@ +Shader "UI/TrueShadow-Normal" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + + _StencilComp ("Stencil Comparison", Float) = 8 + _Stencil ("Stencil ID", Float) = 0 + _StencilOp ("Stencil Operation", Float) = 0 + _StencilWriteMask ("Stencil Write Mask", Float) = 255 + _StencilReadMask ("Stencil Read Mask", Float) = 255 + + _ColorMask ("Color Mask", Float) = 15 + + [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 + } + + SubShader + { + Tags + { + "Queue"="Transparent" + "IgnoreProjector"="True" + "RenderType"="Transparent" + "PreviewType"="Plane" + "CanUseSpriteAtlas"="True" + } + + Stencil + { + Ref [_Stencil] + Comp [_StencilComp] + Pass [_StencilOp] + ReadMask [_StencilReadMask] + WriteMask [_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest [unity_GUIZTestMode] + Blend One OneMinusSrcAlpha + ColorMask [_ColorMask] + + Pass + { + CGPROGRAM + #include "TrueShadow.cginc" + #pragma vertex vert + #pragma fragment frag + ENDCG + } + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Normal.shader.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Normal.shader.meta new file mode 100644 index 0000000..395c49d --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Normal.shader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d81692c4b57a49aabf43f3423dc99e51 +timeCreated: 1594094098 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Screen.shader b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Screen.shader new file mode 100644 index 0000000..0c13328 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Screen.shader @@ -0,0 +1,54 @@ +Shader "UI/TrueShadow-Screen" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + + _StencilComp ("Stencil Comparison", Float) = 8 + _Stencil ("Stencil ID", Float) = 0 + _StencilOp ("Stencil Operation", Float) = 0 + _StencilWriteMask ("Stencil Write Mask", Float) = 255 + _StencilReadMask ("Stencil Read Mask", Float) = 255 + + _ColorMask ("Color Mask", Float) = 15 + + [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 + } + + SubShader + { + Tags + { + "Queue"="Transparent" + "IgnoreProjector"="True" + "RenderType"="Transparent" + "PreviewType"="Plane" + "CanUseSpriteAtlas"="True" + } + + Stencil + { + Ref [_Stencil] + Comp [_StencilComp] + Pass [_StencilOp] + ReadMask [_StencilReadMask] + WriteMask [_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest [unity_GUIZTestMode] + Blend OneMinusDstColor One + ColorMask [_ColorMask] + + Pass + { + CGPROGRAM + #include "TrueShadow.cginc" + #pragma vertex vert + #pragma fragment frag + ENDCG + } + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Screen.shader.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Screen.shader.meta new file mode 100644 index 0000000..6a4983a --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow-Screen.shader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f15b379e29b64cb1ae050f22d002148a +timeCreated: 1610185734 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow.cginc b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow.cginc new file mode 100644 index 0000000..b3f9651 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow.cginc @@ -0,0 +1,62 @@ +#pragma target 2.0 + +#include "UnityCG.cginc" +#include "UnityUI.cginc" + +#pragma multi_compile_local _ UNITY_UI_CLIP_RECT +#pragma multi_compile_local _ UNITY_UI_ALPHACLIP + +struct appdata_t +{ + float4 vertex : POSITION; + float4 color : COLOR; + float2 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +struct v2f +{ + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + float2 texcoord : TEXCOORD0; + float4 worldPosition : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +sampler2D _MainTex; +fixed4 _TextureSampleAdd; +float4 _ClipRect; +float4 _MainTex_ST; + +v2f vert(appdata_t v) +{ + v2f OUT; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); + OUT.worldPosition = v.vertex; + OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); + OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); + OUT.color.a = v.color.a; + OUT.color.rgb = v.color.rgb * v.color.a; + + return OUT; +} + +fixed4 frag(v2f IN) : SV_Target +{ + float4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; + + #ifdef UNITY_UI_CLIP_RECT + color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); + #endif + + #ifdef UNITY_UI_ALPHACLIP + clip (color.a - 0.001); + #endif + + #ifdef DO_BLEND_POSTPROCESS + DO_BLEND_POSTPROCESS + #endif + + return color; +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow.cginc.meta b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow.cginc.meta new file mode 100644 index 0000000..ec45305 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Resources/Shaders/TrueShadow.cginc.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3521747ea3d847a58d919a5a4d9f0fc6 +timeCreated: 1594096488 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts.meta new file mode 100644 index 0000000..ad432eb --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: adf483f749616b14ab22fc1bac9a154e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/DebugSettings.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/DebugSettings.cs new file mode 100644 index 0000000..2cbf7fb --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/DebugSettings.cs @@ -0,0 +1,78 @@ +#if LETAI_TRUESHADOW_DEBUG +using System; +using UnityEngine; +#if UNITY_EDITOR +using System.IO; +using UnityEditor; + +#endif + +namespace LeTai.TrueShadow +{ +[Serializable] +public class DebugSettings +{ + const string FILE_PATH = "ProjectSettings/TrueShadowDebugSettings.json"; + + static DebugSettings instance; + + public static DebugSettings Instance + { + get + { + if (instance == null) + { +#if UNITY_EDITOR + try + { + if (File.Exists(FILE_PATH)) + instance = JsonUtility.FromJson(File.ReadAllText(FILE_PATH)); + else + Create(); + } + catch (Exception) + { + Create(); + } +#else + Create(); +#endif + } + + return instance; + } + } + + static void Create() + { + instance = new DebugSettings(); + instance.Save(); + } + + public bool showObjects = true; + + void Save() + { +#if UNITY_EDITOR + File.WriteAllText(FILE_PATH, JsonUtility.ToJson(this, true)); +#endif + } + +#if UNITY_EDITOR + [MenuItem("Tools/Show Objects")] + static void ShowObjects() + { + Instance.showObjects = true; + Instance.Save(); + } + + [MenuItem("Tools/Hide Objects")] + static void HideObjects() + { + Instance.showObjects = false; + Instance.Save(); + } +#endif +} +} +#endif diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/DebugSettings.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/DebugSettings.cs.meta new file mode 100644 index 0000000..fa54428 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/DebugSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15f8726536514604185aa6c94cb75b8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor.meta new file mode 100644 index 0000000..5012873 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 711abe41546958b459a552baad798f17 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/AutoCustomScriptingDefine.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/AutoCustomScriptingDefine.cs new file mode 100644 index 0000000..979f7cf --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/AutoCustomScriptingDefine.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor; + +namespace LeTai.TrueShadow.Editor +{ +[InitializeOnLoad] +public static class AutoCustomScriptingDefine +{ + internal static readonly HashSet SYMBOLS = new HashSet {"LETAI_TRUESHADOW"}; + + static AutoCustomScriptingDefine() + { + Apply(); + } + + public static void Apply() + { + AddMissingSymbols(EditorUserBuildSettings.activeBuildTarget); + } + + static void AddMissingSymbols(BuildTarget buildTarget) + { + var currentGroup = BuildPipeline.GetBuildTargetGroup(buildTarget); + var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentGroup).Split(';').ToList(); + var missing = SYMBOLS.Except(defines).ToList(); + defines.AddRange(missing); + + if (missing.Count > 0) + PlayerSettings.SetScriptingDefineSymbolsForGroup(currentGroup, string.Join(";", defines)); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/AutoCustomScriptingDefine.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/AutoCustomScriptingDefine.cs.meta new file mode 100644 index 0000000..356c2ca --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/AutoCustomScriptingDefine.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 150508a406864b6ca9ad4e38e75cb333 +timeCreated: 1597477199 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/EditorProperty.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/EditorProperty.cs new file mode 100644 index 0000000..d73ac6e --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/EditorProperty.cs @@ -0,0 +1,59 @@ +using System; +using System.Globalization; +using System.Reflection; +using UnityEditor; + +namespace LeTai.TrueShadow.Editor +{ +public class EditorProperty +{ + public readonly SerializedProperty serializedProperty; + + readonly SerializedObject serializedObject; + readonly MethodInfo propertySetter; + readonly SerializedProperty dirtyFlag; + + public EditorProperty(SerializedObject obj, string name) + { + var propertyName = char.ToLowerInvariant(name[0]) + name.Substring(1); + serializedObject = obj; + serializedProperty = serializedObject.FindProperty(propertyName); + propertySetter = serializedObject.targetObject.GetType().GetProperty(name).SetMethod; + dirtyFlag = serializedObject.FindProperty("modifiedFromInspector"); + } + + public void Draw() + { + using (var scope = new EditorGUI.ChangeCheckScope()) + { + EditorGUILayout.PropertyField(serializedProperty); + + if (!scope.changed) + return; + + dirtyFlag.boolValue = true; + serializedObject.ApplyModifiedProperties(); + + foreach (var target in serializedObject.targetObjects) + { + switch (serializedProperty.propertyType) + { + case SerializedPropertyType.Float: + propertySetter.Invoke(target, new object[] {serializedProperty.floatValue}); + break; + case SerializedPropertyType.Enum: + propertySetter.Invoke(target, new object[] {serializedProperty.enumValueIndex}); + break; + case SerializedPropertyType.Boolean: + propertySetter.Invoke(target, new object[] {serializedProperty.boolValue}); + break; + case SerializedPropertyType.Color: + propertySetter.Invoke(target, new object[] {serializedProperty.colorValue}); + break; + default: throw new NotImplementedException(); + } + } + } + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/EditorProperty.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/EditorProperty.cs.meta new file mode 100644 index 0000000..f2f6944 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/EditorProperty.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1f9db8b2544c48c2a7f13b46f27236e8 +timeCreated: 1594635819 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/InlineToolbar.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/InlineToolbar.cs new file mode 100644 index 0000000..ef96e7e --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/InlineToolbar.cs @@ -0,0 +1,67 @@ +using UnityEditor; +using UnityEngine; + +namespace LeTai.TrueShadow.Editor +{ +[CustomPropertyDrawer(typeof(InsetToggleAttribute))] +public class InsetToggle : InlineToolbar +{ + static readonly Texture OUTER_SHADOW_TEXTURE = Utility.FindAsset("Outer Shadow"); + static readonly Texture INNER_SHADOW_TEXTURE = Utility.FindAsset("Inner Shadow"); + + static InsetToggle() + { + textures = new[] {OUTER_SHADOW_TEXTURE, INNER_SHADOW_TEXTURE}; + } +} + +public class InlineToolbar : PropertyDrawer +{ + protected static Texture[] textures; + + static readonly GUIStyle LABEL_STYLE = new GUIStyle(EditorStyles.label) + {alignment = TextAnchor.MiddleLeft,}; + + public override void OnGUI(Rect position, SerializedProperty property, + GUIContent label) + { + using (var propScope = new EditorGUI.PropertyScope(position, label, property)) + { + int id = GUIUtility.GetControlID(FocusType.Keyboard, position); + var lableRect = position; + lableRect.y += (lableRect.height - EditorGUIUtility.singleLineHeight) / 2; + lableRect.height = EditorGUIUtility.singleLineHeight; + var toolbarRect = EditorGUI.PrefixLabel(lableRect, id, propScope.content, LABEL_STYLE); + toolbarRect.width = EditorGUIUtility.singleLineHeight * 4f; + toolbarRect.height = position.height; + toolbarRect.y = position.y; + + using (var changeScope = new EditorGUI.ChangeCheckScope()) + { + var isOn = GUI.Toolbar(toolbarRect, property.boolValue ? 1 : 0, textures) == 1; + var changed = changeScope.changed; + + if (Event.current.type == EventType.KeyDown && + GUIUtility.keyboardControl == id) + { + if (Event.current.keyCode == KeyCode.Return || + Event.current.keyCode == KeyCode.KeypadEnter || + Event.current.keyCode == KeyCode.Space) + { + changed = GUI.changed = true; + isOn = !isOn; + } + } + + if (changed) + property.boolValue = isOn; + } + } + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUIUtility.singleLineHeight * 2f; + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/InlineToolbar.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/InlineToolbar.cs.meta new file mode 100644 index 0000000..ba82f12 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/InlineToolbar.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ea4346ac17c04991bf233f1dc0def012 +timeCreated: 1602580020 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/KnobPropertyDrawer.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/KnobPropertyDrawer.cs new file mode 100644 index 0000000..d45cead --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/KnobPropertyDrawer.cs @@ -0,0 +1,184 @@ +using System; +using System.Reflection; +using UnityEditor; +using UnityEngine; +using static LeTai.TrueShadow.Math; +using EGU = UnityEditor.EditorGUIUtility; + +namespace LeTai.TrueShadow.Editor +{ +[CustomPropertyDrawer(typeof(KnobAttribute))] +public class KnobPropertyDrawer : PropertyDrawer +{ + public static bool procrastinationMode = false; + + static readonly Texture2D KNOB_BG_TEXTURE = Utility.FindAsset("Knob_BG"); + static readonly Texture2D KNOB_FG_TEXTURE = Utility.FindAsset("Knob_FG"); + + static readonly MethodInfo DO_FLOAT_FIELD_METHOD; + static readonly FieldInfo RECYCLED_EDITOR_PROPERTY; + static readonly FieldInfo FLOAT_FIELD_FORMAT_STRING_CONST; + + static readonly Color KNOB_BG_COLOR; + static readonly Color KNOB_FG_COLOR; + static readonly Color KNOB_FG_COLOR_ACTIVE; + + static KnobPropertyDrawer() + { + var editorGUIType = typeof(EditorGUI); + + const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Static; + + Type[] argumentTypes = { + Assembly.GetAssembly(editorGUIType).GetType("UnityEditor.EditorGUI+RecycledTextEditor"), + typeof(Rect), + typeof(Rect), + typeof(int), + typeof(float), + typeof(string), + typeof(GUIStyle), + typeof(bool) + }; + DO_FLOAT_FIELD_METHOD = editorGUIType.GetMethod("DoFloatField", flags, null, argumentTypes, null); + RECYCLED_EDITOR_PROPERTY = editorGUIType.GetField("s_RecycledEditor", flags); + FLOAT_FIELD_FORMAT_STRING_CONST = editorGUIType.GetField("kFloatFieldFormatString", flags); + + if (EGU.isProSkin) + { + KNOB_BG_COLOR = new Color(.164f, .164f, .164f); + KNOB_FG_COLOR = new Color(.701f, .701f, .701f); + KNOB_FG_COLOR_ACTIVE = new Color(.49f, .67f, .94f); + } + else + { + KNOB_BG_COLOR = new Color(.941f, .941f, .941f); + KNOB_FG_COLOR = new Color(.239f, .239f, .239f); + KNOB_FG_COLOR_ACTIVE = new Color(.054f, .274f, .549f); + } + } + + static float DoFloatFieldInternal(Rect position, + Rect dragHotZone, + int id, + float value, + string formatString = null, + GUIStyle style = null, + bool draggable = true) + { + style = style ?? EditorStyles.numberField; + formatString = formatString ?? (string) FLOAT_FIELD_FORMAT_STRING_CONST.GetValue(null); + + var editor = RECYCLED_EDITOR_PROPERTY.GetValue(null); + + return (float) DO_FLOAT_FIELD_METHOD.Invoke(null, new[] { + editor, + position, + dragHotZone, + id, + value, + formatString, + style, + draggable + }); + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + if (!(attribute is KnobAttribute)) return; + + KnobProperty(position, label, property, Vector2.right); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return ControlHeight; + } + + static float ControlHeight => EGU.singleLineHeight * 2.0f; + static float KnobSize => EGU.singleLineHeight * 2.5f; + static float KnobYOffset => (ControlHeight - KnobSize) / 2; + + static Color Lighten(Color color, float amount) + { + Color.RGBToHSV(color, out var h, out var s, out var v); + return Color.HSVToRGB(h, s, v + amount); + } + + public static void KnobProperty(Rect rect, GUIContent label, SerializedProperty prop, Vector2 zeroVector) + { + float angle = prop.floatValue; + float prevAngle = angle; + + using (var propScope = new EditorGUI.PropertyScope(rect, label, prop)) + using (var changeScope = new EditorGUI.ChangeCheckScope()) + { + var labelRect = new Rect(rect) { + y = rect.y + (ControlHeight - EGU.singleLineHeight) / 2, + height = EGU.singleLineHeight + }; + + int fieldId = GUIUtility.GetControlID(FocusType.Keyboard, labelRect); + var fieldRect = EditorGUI.PrefixLabel(labelRect, fieldId, propScope.content); + labelRect.xMax = fieldRect.x; + fieldRect.x += ControlHeight; + fieldRect.width -= ControlHeight; + + + Rect knobRect = new Rect(rect.x + EGU.labelWidth + KnobYOffset, + rect.y + KnobYOffset, + KnobSize, KnobSize); + int knobId = GUIUtility.GetControlID(FocusType.Passive, knobRect); + + if (Event.current != null) + { + if (Event.current.type == EventType.MouseDown && knobRect.Contains(Event.current.mousePosition)) + { + GUIUtility.hotControl = knobId; + + angle = Angle360(zeroVector, Event.current.mousePosition - knobRect.center); + } + else if (Event.current.type == EventType.MouseUp && GUIUtility.hotControl == knobId) + { + GUIUtility.hotControl = 0; + } + else if (Event.current.type == EventType.MouseDrag && GUIUtility.hotControl == knobId) + { + angle = Angle360(zeroVector, Event.current.mousePosition - knobRect.center); + } + else if (Event.current.type == EventType.Repaint) + { + var notRotated = GUI.matrix; + var oldColor = GUI.color; + var highlighted = GUIUtility.hotControl == knobId || + GUIUtility.hotControl == fieldId || + GUIUtility.keyboardControl == fieldId; + + GUIUtility.RotateAroundPivot(angle, knobRect.center); + GUI.color = KNOB_BG_COLOR; + GUI.DrawTexture(knobRect, KNOB_BG_TEXTURE, ScaleMode.ScaleToFit, true, 1); + GUI.color = highlighted ? KNOB_FG_COLOR_ACTIVE : KNOB_FG_COLOR; + if (procrastinationMode) GUI.color = Color.red; + GUI.DrawTexture(knobRect, KNOB_FG_TEXTURE, ScaleMode.ScaleToFit, true, 1); + + if (!procrastinationMode) + GUI.matrix = notRotated; + GUI.color = oldColor; + } + + if (angle != prevAngle) GUI.changed = true; + } + + + angle = DoFloatFieldInternal( + fieldRect, + labelRect, + fieldId, + angle + ); + + + if (changeScope.changed) prop.floatValue = angle; + } + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/KnobPropertyDrawer.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/KnobPropertyDrawer.cs.meta new file mode 100644 index 0000000..a95cec5 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/KnobPropertyDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 07c24ad14ec14d40ab304c80ba6af657 +timeCreated: 1594636852 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/LeTai.TrueShadow.Editor.asmdef b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/LeTai.TrueShadow.Editor.asmdef new file mode 100644 index 0000000..e090287 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/LeTai.TrueShadow.Editor.asmdef @@ -0,0 +1,16 @@ +{ + "name": "LeTai.TrueShadow.Editor", + "references": [ + "LeTai.TrueShadow" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/LeTai.TrueShadow.Editor.asmdef.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/LeTai.TrueShadow.Editor.asmdef.meta new file mode 100644 index 0000000..b4d4e78 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/LeTai.TrueShadow.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a3c0f23110aac1b4384b9377b63b6595 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/MigrateToV0_5Window.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/MigrateToV0_5Window.cs new file mode 100644 index 0000000..edacad7 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/MigrateToV0_5Window.cs @@ -0,0 +1,104 @@ +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace LeTai.TrueShadow.Editor +{ +public class MigrateToVV1Window : EditorWindow +{ + private const string SHOW_ON_START_EDITOR_PREFS_KEY = "LeTai.TrueShadow.MigrateToVV1WindowShown"; + + [MenuItem("Tools/TrueShadow/Migrate To v1")] + public static MigrateToVV1Window ShowWindow() + { + var window = GetWindow(true, "True Shadow"); + window.position = new Rect(600, 400, 600, 400); + return window; + } + + [InitializeOnLoadMethod] + private static void InitializeOnLoadMethod() + { + RegisterWindowCheck(); + } + + private static void RegisterWindowCheck() + { + if (!EditorApplication.isPlayingOrWillChangePlaymode) + { + EditorApplication.update += CheckShowWindow; + } + } + + private static void CheckShowWindow() + { + EditorApplication.update -= CheckShowWindow; + if (EditorPrefs.GetBool(SHOW_ON_START_EDITOR_PREFS_KEY, true)) + { + ShowWindow(); + } + } + + void OnDestroy() + { + EditorPrefs.SetBool(SHOW_ON_START_EDITOR_PREFS_KEY, false); + } + + bool haveBackup; + + private void OnGUI() + { + GUILayout.Label("Migrate to v1", EditorStyles.largeLabel); + EditorGUILayout.Separator(); + EditorGUILayout.HelpBox( + "In v1, Blend Mode was changed to produce better looking shadows, as well as better compatibility with 3rd parties asset. As a side effect, most shadows should now use Color Bleed Mode: . This tool attempt to do this automatically.\n\n" + + "All True Shadows in currently loaded scenes will be migrated. You may want to load all scenes you want to fix before migrating. All True Shadows in prefabs will also be migrated.\n\n" + + "All True Shadows in prefabs will also be migrated.\n\n" + + "You may access this dialog later from the Tools menu.", + MessageType.Info + ); + EditorGUILayout.Separator(); + EditorGUILayout.HelpBox( + "!!! MAKE SURE TO BACK UP YOUR PROJECT BEFORE USE !!!\n\n" + + "This tool will modify your project files. Please backup your project before use. If you are unsure how to do this, do NOT use this tool! Manually change any problematic shadows Color Bleed mode to Black instead!", + MessageType.Warning); + EditorGUILayout.Separator(); + haveBackup = EditorGUILayout.ToggleLeft("I have backed up the project and can undo any changes done by the tool", haveBackup); + if (haveBackup) + { + if (GUILayout.Button("Migrate to v1")) + MigrateToV1(); + } + } + + public static void MigrateToV1() + { + var allPrefabs = AssetDatabase.FindAssets("t:Prefab"); + foreach (var guid in allPrefabs) + { + var path = AssetDatabase.GUIDToAssetPath(guid); + var prefabRoot = PrefabUtility.LoadPrefabContents(path); + var changed = false; + + foreach (var shadow in prefabRoot.GetComponentsInChildren()) + { + shadow.ColorBleedMode = ColorBleedMode.Black; + changed = true; + } + + if (changed) + PrefabUtility.SaveAsPrefabAsset(prefabRoot, path); + + PrefabUtility.UnloadPrefabContents(prefabRoot); + } + + var inScene = Resources.FindObjectsOfTypeAll() + .ToArray(); + Undo.RecordObjects(inScene, "Migrate to 0.5"); + foreach (var shadow in inScene) + { + shadow.ColorBleedMode = ColorBleedMode.Black; + } + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/MigrateToV0_5Window.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/MigrateToV0_5Window.cs.meta new file mode 100644 index 0000000..b41c8a4 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/MigrateToV0_5Window.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 126149305ac84024ac1d713f39caf754 +timeCreated: 1611560835 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/PrefabEventHandler.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/PrefabEventHandler.cs new file mode 100644 index 0000000..acc5fac --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/PrefabEventHandler.cs @@ -0,0 +1,19 @@ +using UnityEditor; + +namespace LeTai.TrueShadow.Editor +{ +[InitializeOnLoad] +class PrefabEventHandler +{ + static PrefabEventHandler() + { + PrefabUtility.prefabInstanceUpdated += go => + { + var shadows = go.GetComponentsInChildren(); + + foreach (var shadow in shadows) + shadow.ApplySerializedData(); + }; + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/PrefabEventHandler.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/PrefabEventHandler.cs.meta new file mode 100644 index 0000000..13d3563 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/PrefabEventHandler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9806fc5a3b18445e9766a68cf69b81c9 +timeCreated: 1600845150 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/ScenceGizmoAutoDisable.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/ScenceGizmoAutoDisable.cs new file mode 100644 index 0000000..26beb76 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/ScenceGizmoAutoDisable.cs @@ -0,0 +1,62 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace LeTai.Asset.TrueShadow.Editor +{ +public static class ScenceGizmoAutoDisable +{ + static readonly string[] NO_GIZMOS_CLASSES = { + "TrueShadow" + }; + + [UnityEditor.Callbacks.DidReloadScripts] + private static void OnScriptsReloaded() + { + var Annotation = Type.GetType("UnityEditor.Annotation, UnityEditor"); + if (Annotation == null) return; + + var ClassId = Annotation.GetField("classID"); + var ScriptClass = Annotation.GetField("scriptClass"); + var Flags = Annotation.GetField("flags"); + var IconEnabled = Annotation.GetField("iconEnabled"); + + Type AnnotationUtility = Type.GetType("UnityEditor.AnnotationUtility, UnityEditor"); + if (AnnotationUtility == null) return; + + var GetAnnotations = AnnotationUtility.GetMethod("GetAnnotations", + BindingFlags.NonPublic | BindingFlags.Public | + BindingFlags.Static); + if (GetAnnotations == null) return; + var SetIconEnabled = AnnotationUtility.GetMethod("SetIconEnabled", + BindingFlags.NonPublic | BindingFlags.Public | + BindingFlags.Static); + if (SetIconEnabled == null) return; + + + Array annotations = (Array) GetAnnotations.Invoke(null, null); + foreach (var a in annotations) + { + int classId = (int) ClassId.GetValue(a); + string scriptClass = (string) ScriptClass.GetValue(a); + int flags = (int) Flags.GetValue(a); + int iconEnabled = (int) IconEnabled.GetValue(a); + + // built in types + if (string.IsNullOrEmpty(scriptClass)) continue; + + // load a json or text file with class names + + const int HasIcon = 1; + bool hasIconFlag = (flags & HasIcon) == HasIcon; + + if (hasIconFlag && + iconEnabled != 0 && + NO_GIZMOS_CLASSES.Contains(scriptClass)) + { + SetIconEnabled.Invoke(null, new object[] {classId, scriptClass, 0}); + } + } + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/ScenceGizmoAutoDisable.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/ScenceGizmoAutoDisable.cs.meta new file mode 100644 index 0000000..c2804f1 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/ScenceGizmoAutoDisable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a1266d64cf1066b47b900e5115a5b75d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/SpreadSliderDrawer.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/SpreadSliderDrawer.cs new file mode 100644 index 0000000..ba950de --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/SpreadSliderDrawer.cs @@ -0,0 +1,87 @@ +using UnityEditor; +using UnityEngine; +using static UnityEditor.EditorGUI; + +namespace LeTai.TrueShadow.Editor +{ +[CustomPropertyDrawer(typeof(SpreadSliderAttribute))] +public class SpreadSliderDrawer : PropertyDrawer +{ + const float SLIDER_SPACING = 5; + const float MARKER_HEIGHT = 6; + const float MARKER_ALPHA = .75f; + const float MARKER_FILLET = 2; + + static readonly Vector4 START_RADII = new Vector4(MARKER_FILLET, 0, 0, MARKER_FILLET); + static readonly Vector4 END_RADII = new Vector4(0, MARKER_FILLET, MARKER_FILLET, 0); + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + using (var propScope = new PropertyScope(position, label, property)) + using (var changeScope = new ChangeCheckScope()) + { + var controlPosition = PrefixLabel(position, propScope.content); + + var floatFieldWidth = Mathf.Min(EditorGUIUtility.fieldWidth, controlPosition.width); + var sliderPosition = new Rect(controlPosition) + {width = controlPosition.width - floatFieldWidth - SLIDER_SPACING}; + + const float marker1 = .8f; + const float marker2 = .95f; + + DrawMarkers(sliderPosition, + (marker1, new Color(1.00000f, 0.60392f, 0.01961f, MARKER_ALPHA)), + (marker2, new Color(1.00000f, 0.25490f, 0.20784f, MARKER_ALPHA))); + + var newVal = Slider(controlPosition, + GUIContent.none, + property.floatValue, + 0, 1); + + if (!Event.current.control && !Event.current.alt) + { + var dist1 = (newVal - marker1) * sliderPosition.width; + var dist2 = (newVal - marker2) * sliderPosition.width; + if (0 < dist1 && dist1 < 4) + newVal = marker1; + if (0 < dist2 && dist2 < 4) + newVal = marker2; + } + + if (changeScope.changed) + property.floatValue = newVal; + } + } + + void DrawMarkers(Rect sliderPosition, params (float, Color)[] markers) + { + var hPad = GUI.skin.horizontalSliderThumb.fixedWidth / 2f; + var markerXStart = sliderPosition.x + hPad; + var markerXEnd = sliderPosition.width - hPad * 2; + var vPad = (sliderPosition.height - MARKER_HEIGHT) / 2f; + var markerYStart = sliderPosition.y + vPad; + var markerHeight = sliderPosition.height - vPad * 2; + for (var i = 0; i < markers.Length; i++) + { + var (offset, color) = markers[i]; + + var x = markerXStart + markerXEnd * offset; + var width = i < markers.Length - 1 + ? sliderPosition.width * (markers[i + 1].Item1 - offset) - 1 + : sliderPosition.xMax - x; + var position = new Rect { + x = x, + y = markerYStart, + width = width, + height = markerHeight + }; + + var radii = i == 0 ? START_RADII : END_RADII; + + GUI.DrawTexture(position, + Texture2D.whiteTexture, ScaleMode.StretchToFill, true, 0, color, + Vector4.zero, radii); + } + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/SpreadSliderDrawer.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/SpreadSliderDrawer.cs.meta new file mode 100644 index 0000000..a98c9e4 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/SpreadSliderDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3292983b33504803ae542a485f446019 +timeCreated: 1615190623 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures.meta new file mode 100644 index 0000000..332da49 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1cd90590774134c4ea35245f256f89e9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Inner Shadow.png b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Inner Shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..79747184d3da136c5f00827a24fe9d6d4123b6ba GIT binary patch literal 459 zcmV;+0W|)JP)mkH`(lJ zQtS`Q>}8n$J7;E?vlJS)N-_RZzyYugtOBzma<+jwPy;TEF@b$qD*f6<0W1N>z*^49 zxdYCOG0#Z=Hu6)M_kb@T$Y^ZGtO6B5oB&<-vEp?;1ombA3G_3XqcH+`EdsB=b(k)! z9o^f=jrYxj26LB*=ohDXU|AmLaRBRTdl6CCw0!{YKnv)o->?8>V9#G$K7e};589Vm z6KLpq5llDbG+BV@BmiGVCQWT8yNDk3+zCy?$QlIDSELP@-vCc~mv#+AY7ju6Id3R( zYyKv%OvX;-AplyM^QI!t&D?4jfQ}&AdCYlQ0HduIq}+130+>+R{~h4ZRR9zK69?d` zpSW5vi(FmVP6D`9>sLy7Xj5a>sf@~!4V(5YRoNTB zy*z$2ab>xa`!U%CR)CoiP3=kg09?iWm=qeXfN!jwf))>hn27)Y002ovPDHLkV1mNx B!=?ZL literal 0 HcmV?d00001 diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Inner Shadow.png.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Inner Shadow.png.meta new file mode 100644 index 0000000..ca90dbe --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Inner Shadow.png.meta @@ -0,0 +1,130 @@ +fileFormatVersion: 2 +guid: b11fa2df94e30d244aadf3846cb15aeb +labels: +- TrueShadowEditorResources +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 1 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 21fab31b34964614680dcb53be2692d1 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_BG.png b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_BG.png new file mode 100644 index 0000000000000000000000000000000000000000..136b7bcf3a3a760478117bcd61706a429681166e GIT binary patch literal 1090 zcmV-I1ikx-P)&zj@wwW`6H&KK0xDnwj@`zQ37y=Qr~_LnIQ3L?V$$q>&^xr_(eI zt;=kn8(0o31C{_Yfli=9%KQMn0Uv=u;5je=JOJK@G%iV6OH`DAUBGr=qlo5$5%V_C z4?Oa1Xqf~wz3CvZ7g*t|(NR1DE&=DI*E&-M0jB`Hz!6}Y52qA-2F?O!fUhnM*#xv= z1kCwK(lp(Zrs+^x97AG5lGhKbz#0K_un4#etaJPMym24c4ZOA-M}|wuu;e4m7P_te|*^%@(gP;}Z`!yeRTO3`Z2s_mqR$?9r~%?3 zJ|5H-*7iVC?gg;e)w+uCQs%C+8te!<>J=^&ub2+X#CX*SeGcQEU0|Wa;!Nhr&UeB`>|#!**{I;y!dP- zOK3-jEG(ziT*NQ3k?K9PNT%Cf>UBWU_-l3b)Bos_1?(md#{_1&xFrkvT3^OOVeO`3%Z2>(eq^e;0{FB9TZW5@{g7AB>XD9e&wet^fc407*qo IM6N<$f@{PCkN^Mx literal 0 HcmV?d00001 diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_BG.png.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_BG.png.meta new file mode 100644 index 0000000..e3403df --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_BG.png.meta @@ -0,0 +1,130 @@ +fileFormatVersion: 2 +guid: 413bbe8f3993d194682180c4acfc0d1e +labels: +- TrueShadowEditorResources +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 16 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 1 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 21fab31b34964614680dcb53be2692d1 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_FG.png b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/Knob_FG.png new file mode 100644 index 0000000000000000000000000000000000000000..92a73f81bcceddc0cd93dc036ace03428c4979db GIT binary patch literal 1235 zcmV;^1T6cBP)(B$0Pqul9{_v_;GA4X zrP1gtQ9lKI62R*KUIwtCqK#hxoCNSOfUmUtW&Ha4@qn$maF5g1#*M@i1MdB{1Ehe?hr9&lW2%(dxbR(u#p z0ke^^Sn^QwB&SFY1zf9!#O8cSe~aSFKnb{y}n3g?ys(r1xn@hxcqH<4U$9KT9(M~&j;Wry_bPUoK1#5x7MkL0Bi@wW6qyu55F z5%H4vnw)^m5K|^XDO*ySsR=ky@|&#AHP2V}JHEBV=OhHoUY%wf2d5JfFE8%5^Jm?> z_SGTaWXZZWmY{%RCF|j23Id*YoSI5dyu3^~zFyD@s0}7O1>mc~$G=G|?`BoIkr@K; zBY^dVZnitm@E?4^gnN|!zUtJQ35y5ut?7K`K0{AvM_@MM*2zaL`OjX_I24Jk0Nwzw z55PtliJiCx;4FYw8jVIPic2J)aMqJE39flrkl}cTP@0dId^%{R@E)grR$(g!*(f^& z;E>`uCwvVw0zTu^&naxh;241Cm3ANZHP8t7m{b2&VJkG-0K6H%=0m;)8Uc$tX@APX zn+n+%#=QZ2S>tP<5pb+{UQCoMQV7@Q7d^KV}Rjeyy?l$-|vj3{h{W*We)05%tV4KxB? zcIsOdwnFm_fDZ%M{KeNmBjBu4->a|{gSP{`UVi6mpb_xAQ`f#Wg(eGKO#=7;!0n2* zgB(yQMWP+Q7or*ekX)N+Bzhak_efqQd9#Zs%r%lHNv?=3;8CaJ!wHA;^YV`4^U;2W z^q-dFwmEYEHd#o&d$Nu%7Fw^DW4o#Hnj_xDPU1zSFwD!gHDjK@s#-=<-PRnGjrETl zA7@h8SVxK3rw zl(8eeJqQTNM!LZ}>DM9i8{$*XHv1)DR-P-F&XiPygAvjk4z@{iSQ1|<+3u%+=rYkp z6M$HIHz(TUWmqC1ol?C%k@T%yBu6^f8j&_fQ#5LVHTU)*+ttwB%Ge_ zNv!N4*>FbA!`wB=hQ-p5_fJcA4X{3hNVv|zXD9lZaGg@h|FeaZ|AzsL7LHbAQ+Y`? xdA^qzm`0$3%CHB1&#sxna>8m8{jeU0NASX;sCn9 zRp9uXL1)=(;5P873V>yPOTk|NcY&wMa8utt5@VcyW=|5|Bf{VPm6C27JSC5X&~!$v`Rky$mMIcAWqkfdvGBx;5Zu zkHszO22f_BF;LVAAPGb)L#lffr2PM0FyNzUpxi4F;>)ql5|Gs&7oqoT?L|jZcZAa& z81OPQ48SW*o>?>iwPt3U*?i$(8tPunF3r=D&9anIM?O&_60>hQ3kKk!8pj@R4Osn$ zmV82cN@-PHc27s|y0R|xn6HBW-j-Wu{T3K!8d%r2!?UI)egKbDtFDg9d6#IZtnFLi zL&!RX`x|OV)>8KJvKq*qdbqC6?rA~XQV>=2SA-KV&W{A{CGb4YvJe|4nlEBxJOx0S zGVa*t*?zB(uVL^%Xzuj=OQlQRG-y78&DS)c(TLd-N-<J_7G__4A$3>(^K7REV-p z&_$#(Wi`(Cdmp#t$G;2SONfJM0E~7Mh`(rpIU+`k4P(VAjdgeWz3vVyQg42Z0ZY)p zjB^o2Ds8mPZBMZ5J$MWl!FV}c3sDz#-vC&0-<4DW6h8qCk;%CTbttC*00001 z&2HRO7(LgXnI^QfL`8@#UDCHeEI>R0+af@)pbwBN(mkvYLcpdH4^RoDE_eg71kzPS zX{0V%glHx+^^EO{Jo>)T$>C1o37vE-u4LKq*w^=*@AuyO1^@RJ8vNVa+e*Wm0B!(y z7r+|;E}Roj1%Uei?gIFBcX#)Lb4%)W3#It8GOCJ32?eMUi4f1k?t6YWJt41(7r*Cyj|+Bj6RX zbGCg4T^QIVb?)2W{yVGw{JLudyhf^Dle$-SJTuTeyUxXFW<~})nskkTSM8h|keVc1 z*!c*+$fCxPkTo@OYA2$r1Y{)rk{n=Rk>b!u$nCnO5IUuZP^vLVGEf=H(7ew_d zl6Xepwvz;0N!%+<>Qj=sgKxl2?6^S0IVqj<0WVVs<2+z$hbKWfg~XT4Ax=y~GV&pv z^8uTr`aQ}42IBQA13tBoxM>7DBeI(JCtV{TxA@KpO+|tXCizC)H!KP?;Gf#_KIM0k zvaS)}Qs=dcfr0K*NE{FenO$f0zWaw+L|BKlzhzpKNt2J??LHdVKKX+UJNJo%-23So z0T2=b=k+u_4nzW{z&v@H%__S_fP;&2FG}2J6X}cEv$n*p5#Z8x4TwZaPLdA<*2(J8 ztkB;HT_ZpjG{S=At{~T({pD;%UDN@v_{~+>l#8QQ(@Nt2g4>&$s8d_N(}N3 zZVGwV2=JyV4p}g*$-MW|k&aCAfJja*BWc%(P&r>pcLFA-><*o@ zV{Ys29%RV5O{`FYEU3`m+BC{^zH4|iV( zswLXN!NC)HTC+ijq!oDTnWokLz;_F_k=CNc7~|gF-UIRaIv-GgndAbsXw1xqI03%^ z_Okwt5jQ=kFXey|P*}g3{UbNkrE!(NRN@Q#q_)S`cO$~Sv=$JTbRmY-i z)B*WEK(ieFH-NtY{0X25p^xqFq3stonD=wEBI(yBNJ~MfR9IR; z-j4|aQhn?0ua9%OppVvDV=M_SO!6v?N#dhCFIRQ0MbdQwv_7MKfW8q${Y%;nRw0$t zch!afwy!De#ZTy{X?HeI5kp1*GXctd`@r2cb0V*zeq5b7s{;waqks#hpxmG)2RQdT zZ3qYn`zq;!BMW=JC&$&NJ$k1{;7ZYqTJrWXK^l(-)K^8d08NmPu`$lN1J+{8+&=q{3bS@XLgLJYRpN9MDEN~&r&Mu zwK#<_eMq~nu+BH^gMBgyU7b})g5LLUss8|xm5FM5o#3GzVqzt@UFYXZy~{1XWvj`O6C>JqtXfgEfJpSTok8YJ|8d(~A)g$-YbS z4ek&JGXLhrKvSVl9j49=xKPr|VhgkVv8BRCqy+^5FP+$BoSXnG$P!`iq)1SS zKVUyl5D}aRL9}pouPp(ARL;W>&%G;A|3K=`AUK|@S#ZLnCKri()dB}Mm5?|gr#BHn z#$|i}5-O=Pz>LD479s@-S-Aj(fR2*=={I$KfG0#6661o2*|GJAWw*!GAw~k{gbYYi zcrKu6E^7c1zqzz>`9A*>1e-}I@78qa^K*eNOMrTECxT;hAw+31quWy;O>ux(!e_9L zP=~2wf`a-ERAd1F`~%*a VtzDFdmc#%6002ovPDHLkV1iN^-u?gp literal 0 HcmV?d00001 diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/True Shadow Icon.png.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/True Shadow Icon.png.meta new file mode 100644 index 0000000..e594769 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Textures/True Shadow Icon.png.meta @@ -0,0 +1,128 @@ +fileFormatVersion: 2 +guid: 098c7906709c37c4bba842c34581765c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 21fab31b34964614680dcb53be2692d1 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/TrueShadowEditor.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/TrueShadowEditor.cs new file mode 100644 index 0000000..fe27347 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/TrueShadowEditor.cs @@ -0,0 +1,104 @@ +using UnityEditor; +using UnityEngine; +using static UnityEditor.EditorGUILayout; + +namespace LeTai.TrueShadow.Editor +{ +[CanEditMultipleObjects] +[CustomEditor(typeof(TrueShadow))] +public class TrueShadowEditor : UnityEditor.Editor +{ + EditorProperty insetProp; + EditorProperty sizeProp; + EditorProperty spreadProp; + EditorProperty angleProp; + EditorProperty distanceProp; + EditorProperty colorProp; + EditorProperty blendModeProp; + EditorProperty multiplyCasterAlphaProp; + EditorProperty ignoreCasterColorProp; + EditorProperty colorBleedModeProp; + + GUIContent procrastinateLabel; + + static bool showExperimental; + static bool showAdvanced; + + void OnEnable() + { + insetProp = new EditorProperty(serializedObject, nameof(TrueShadow.Inset)); + sizeProp = new EditorProperty(serializedObject, nameof(TrueShadow.Size)); + spreadProp = new EditorProperty(serializedObject, nameof(TrueShadow.Spread)); + angleProp = new EditorProperty(serializedObject, nameof(TrueShadow.OffsetAngle)); + distanceProp = new EditorProperty(serializedObject, nameof(TrueShadow.OffsetDistance)); + colorProp = new EditorProperty(serializedObject, nameof(TrueShadow.Color)); + blendModeProp = new EditorProperty(serializedObject, nameof(TrueShadow.BlendMode)); + multiplyCasterAlphaProp = new EditorProperty(serializedObject, nameof(TrueShadow.UseCasterAlpha)); + ignoreCasterColorProp = new EditorProperty(serializedObject, nameof(TrueShadow.IgnoreCasterColor)); + colorBleedModeProp = new EditorProperty(serializedObject, nameof(TrueShadow.ColorBleedMode)); + + if (EditorPrefs.GetBool("LeTai_TrueShadow_" + nameof(showExperimental))) + { + showExperimental = EditorPrefs.GetBool("LeTai_TrueShadow_" + nameof(showExperimental), false); + showAdvanced = EditorPrefs.GetBool("LeTai_TrueShadow_" + nameof(showAdvanced), false); + } + + procrastinateLabel = new GUIContent("Procrastinate", "A bug that is too fun to fix"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + var ts = (TrueShadow) target; + + insetProp.Draw(); + sizeProp.Draw(); + spreadProp.Draw(); + angleProp.Draw(); + distanceProp.Draw(); + colorProp.Draw(); + if (ts.UsingRendererMaterialProvider) + { + using(new EditorGUI.DisabledScope(true)) + LabelField(blendModeProp.serializedProperty.displayName, "Custom Material"); + } + else + { + blendModeProp.Draw(); + } + + using (var change = new EditorGUI.ChangeCheckScope()) + { + showAdvanced = Foldout(showAdvanced, "Advanced Settings", true); + using (new EditorGUI.IndentLevelScope()) + if (showAdvanced) + { + multiplyCasterAlphaProp.Draw(); + ignoreCasterColorProp.Draw(); + colorBleedModeProp.Draw(); + + if (KnobPropertyDrawer.procrastinationMode) + { + var rot = GUI.matrix; + GUI.matrix = Matrix4x4.identity; + KnobPropertyDrawer.procrastinationMode ^= Toggle("Be Productive", false); + GUI.matrix = rot; + } + else + { + KnobPropertyDrawer.procrastinationMode |= Toggle(procrastinateLabel, false); + } + } + + if (change.changed) + { + EditorPrefs.SetBool("LeTai_TrueShadow_" + nameof(showExperimental), showExperimental); + EditorPrefs.SetBool("LeTai_TrueShadow_" + nameof(showAdvanced), showAdvanced); + } + } + + serializedObject.ApplyModifiedProperties(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/TrueShadowEditor.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/TrueShadowEditor.cs.meta new file mode 100644 index 0000000..e92595e --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/TrueShadowEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 120b61c7775149ed9de9c782978f6d61 +timeCreated: 1592985822 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Utility.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Utility.cs new file mode 100644 index 0000000..f12efa0 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Utility.cs @@ -0,0 +1,22 @@ +using UnityEditor; +using UnityEngine; + +namespace LeTai.TrueShadow.Editor +{ +public static class Utility +{ + public static T FindAsset(string assetName) where T : UnityEngine.Object + { + var guids = AssetDatabase.FindAssets("l:TrueShadowEditorResources " + assetName); + if (guids.Length == 0) + { + Debug.LogError($"Asset \"{assetName}\" not found. " + + $"Make sure it have the label \"TrueShadowEditorResources\""); + return null; + } + + var path = AssetDatabase.GUIDToAssetPath(guids[0]); + return AssetDatabase.LoadAssetAtPath(path); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Utility.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Utility.cs.meta new file mode 100644 index 0000000..9dcb3f2 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Editor/Utility.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 09b72999f3c04f898c16bf60deb38c6e +timeCreated: 1602578784 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects.meta new file mode 100644 index 0000000..df32430 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 20daae54a38548b488eb9c2aa1d39507 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/BlurConfig.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/BlurConfig.cs new file mode 100644 index 0000000..a2750e2 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/BlurConfig.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +namespace LeTai.Effects +{ + public class BlurConfig : ScriptableObject + { + + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/BlurConfig.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/BlurConfig.cs.meta new file mode 100644 index 0000000..7d5faca --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/BlurConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1598e613c3d69154db682bbf3c234cb2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/IBlurAlgorithm.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/IBlurAlgorithm.cs new file mode 100644 index 0000000..f24b4c6 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/IBlurAlgorithm.cs @@ -0,0 +1,15 @@ +using UnityEngine; +using UnityEngine.Rendering; + +namespace LeTai.Effects +{ +public interface IBlurAlgorithm +{ + void Configure(BlurConfig config); + + void Blur(CommandBuffer cmd, + RenderTargetIdentifier src, + Rect srcCropRegion, + RenderTexture target); +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/IBlurAlgorithm.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/IBlurAlgorithm.cs.meta new file mode 100644 index 0000000..180729e --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/IBlurAlgorithm.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5bc98ab7243bbdc40a0c52f6d1ed58a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlur.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlur.cs new file mode 100644 index 0000000..82f4758 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlur.cs @@ -0,0 +1,104 @@ +using UnityEngine; +using UnityEngine.Rendering; + +namespace LeTai.Effects +{ +public class ScalableBlur : IBlurAlgorithm +{ + Material material; + ScalableBlurConfig config; + + const int BLUR_PASS = 0; + const int CROP_BLUR_PASS = 1; + + Material Material + { + get + { + if (material == null) + Material = new Material(Shader.Find("Hidden/EfficientBlur")); + + return material; + } + set => material = value; + } + + ~ScalableBlur() + { + if (!material) return; + + if (Application.isPlaying) + Object.Destroy(material); + else + Object.DestroyImmediate(material); + } + + public void Configure(BlurConfig config) + { + this.config = (ScalableBlurConfig) config; + } + + public void Blur(CommandBuffer cmd, + RenderTargetIdentifier src, + Rect srcCropRegion, + RenderTexture target) + { + float radius = config.Radius; + Material.SetFloat(ShaderProperties.blurRadius, radius); + Material.SetVector(ShaderProperties.blurTextureCropRegion, srcCropRegion.ToMinMaxVector()); + + int firstDownsampleFactor = config.Iteration > 0 ? 1 : 0; + int stepCount = Mathf.Max(config.Iteration * 2 - 1, 1); + + int firstIRT = ShaderProperties.intermediateRT[0]; + CreateTempRenderTextureFrom(cmd, firstIRT, target, firstDownsampleFactor); + + // cmd.BlitFullscreenTriangle(src, firstIRT, Material, CROP_BLUR_PASS); + cmd.Blit(src, firstIRT, Material, CROP_BLUR_PASS); + + for (var i = 1; i < stepCount; i++) + { + BlurAtDepth(cmd, i, target); + } + + // cmd.BlitFullscreenTriangle(ShaderProperties.intermediateRT[stepCount - 1], target, Material, BLUR_PASS); + cmd.Blit(ShaderProperties.intermediateRT[stepCount - 1], target, Material, BLUR_PASS); + + CleanupIntermediateRT(cmd, stepCount); + } + + protected virtual void BlurAtDepth(CommandBuffer cmd, int depth, RenderTexture baseTexture) + { + int sizeLevel = Utility.SimplePingPong(depth, config.Iteration - 1) + 1; + sizeLevel = Mathf.Min(sizeLevel, config.MaxDepth); + CreateTempRenderTextureFrom(cmd, ShaderProperties.intermediateRT[depth], baseTexture, sizeLevel); + + // cmd.BlitFullscreenTriangle( + cmd.Blit( + ShaderProperties.intermediateRT[depth - 1], + ShaderProperties.intermediateRT[depth], + Material, + 0 + ); + } + + static void CreateTempRenderTextureFrom(CommandBuffer cmd, + int nameId, + RenderTexture src, + int downsampleFactor) + { + int w = src.width >> downsampleFactor; //= width / 2^downsample + int h = src.height >> downsampleFactor; + + cmd.GetTemporaryRT(nameId, w, h, 0, FilterMode.Bilinear, src.format); + } + + static void CleanupIntermediateRT(CommandBuffer cmd, int amount) + { + for (var i = 0; i < amount; i++) + { + cmd.ReleaseTemporaryRT(ShaderProperties.intermediateRT[i]); + } + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlur.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlur.cs.meta new file mode 100644 index 0000000..ece9834 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlur.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d72465d1220a01e4abccda77619f6763 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlurConfig.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlurConfig.cs new file mode 100644 index 0000000..ffc7ddb --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlurConfig.cs @@ -0,0 +1,110 @@ +using UnityEngine; +using static UnityEngine.Mathf; + +namespace LeTai.Effects +{ +public class ScalableBlurConfig : BlurConfig +{ + [SerializeField] float radius = 4; + [SerializeField] int iteration = 4; + [SerializeField] int maxDepth = 6; + [SerializeField] [Range(0, 256)] float strength; + + /// + /// Distance between the base texel and the texel to be sampled. + /// + public float Radius + { + get { return radius; } + set { radius = Max(0, value); } + } + + /// + /// Half the number of time to process the image. It is half because the real number of iteration must alway be even. Using half also make calculation simpler + /// + /// + /// Must be non-negative + /// + public int Iteration + { + get { return iteration; } + set { iteration = Max(0, value); } + } + + /// + /// Clamp the minimum size of the intermediate texture. Reduce flickering and blur + /// + /// + /// Must larger than 0 + /// + public int MaxDepth + { + get { return maxDepth; } + set { maxDepth = Max(1, value); } + } + + /// + /// User friendly property to control the amount of blur + /// + /// + /// Must be non-negative + /// + public float Strength + { + get { return strength = radius * (3 * (1 << iteration) - 2) / UNIT_VARIANCE; } + set + { + strength = Max(0, value); + SetAdvancedFieldFromSimple(); + } + } + + // With the "correct" unit variance, the edge of the shadow at higher stddev go below 8bit fixed point resolution + // We "wastes" processing power on these. + // TODO: optimize that: + // The maximum distance that will show up is: + // e^(-D^2 / 2R^2) < .5/256 + // => D < 3*sqrt(2*log(2)) * R ~ 3.53223*R + // Can probably stop sooner than that + static readonly float UNIT_VARIANCE = 1f + Sqrt(2f) / 2f; + + /// + /// Calculate size and iteration from strength + /// + protected virtual void SetAdvancedFieldFromSimple() + { + if (strength < 1e-2) + { + iteration = 0; + radius = 0; + return; + } + + var variance = strength * UNIT_VARIANCE; + + // Each level of the pyramid have double the effective radius of the last, so total effective radius would be: + // S = (2^0 + 2^1 +...+ 2^iteration +...+ 2^1 + 2^0) * R + // https://en.wikipedia.org/wiki/1_%2B_2_%2B_4_%2B_8_%2B_%E2%8B%AF + // S = (3 * 2^I - 2) * R + // so: + // I = log((s + 2r)/ (3r))/log(2) + // and: + // R = S / (3 * 2^I - 2) + // + // Experimental result show that best result are obtained with R <= 2^I - 1, so: + // I >= log(1/6 * (sqrt(12S + 1) + 5))/log(2) + // + // There still some artifact at the lower end, not sure how to handle that yet + // TODO: use a different algorithm for low Strength. + + iteration = CeilToInt(Log(1 / 6f * (Sqrt(12 * variance + 1) + 5)) / Log(2)); + + radius = variance / (3 * (1 << iteration) - 2); + } + + void OnValidate() + { + SetAdvancedFieldFromSimple(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlurConfig.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlurConfig.cs.meta new file mode 100644 index 0000000..aa4a5e5 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ScalableBlurConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 506e4e7bc6f8ce24486de8c2732c0a1b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ShaderProperties.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ShaderProperties.cs new file mode 100644 index 0000000..012f4fb --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ShaderProperties.cs @@ -0,0 +1,37 @@ +using UnityEngine; + +namespace LeTai.Effects +{ + public static class ShaderProperties + { + private static bool isInitialized; + + public static int[] intermediateRT; + + public static int blurRadius; + public static int blurTextureCropRegion; + + public static void Init() + { + if (isInitialized) + return; + + + blurRadius = Shader.PropertyToID("_Radius"); + blurTextureCropRegion = Shader.PropertyToID("_CropRegion"); + + isInitialized = true; + } + + public static void Init(int stackDepth) + { + intermediateRT = new int[stackDepth * 2 - 1]; + for (var i = 0; i < intermediateRT.Length; i++) + { + intermediateRT[i] = Shader.PropertyToID(string.Format("TI_intermediate_rt_{0}", i)); + } + + Init(); + } + } +} \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ShaderProperties.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ShaderProperties.cs.meta new file mode 100644 index 0000000..e05f0bf --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Effects/ShaderProperties.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ae61d4a8774bbb41acdf25cded306c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper.meta new file mode 100644 index 0000000..09f2720 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fa263cbfa5524d12a0df2e7befd04f50 +timeCreated: 1593508707 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/AnimatedBiStateButton.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/AnimatedBiStateButton.cs new file mode 100644 index 0000000..0231699 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/AnimatedBiStateButton.cs @@ -0,0 +1,138 @@ +using System; +using UnityEngine; +using UnityEngine.EventSystems; + +public class AnimatedBiStateButton : MonoBehaviour, + IPointerDownHandler, IPointerUpHandler, + IPointerEnterHandler, IPointerExitHandler +{ + public enum State + { + Up, + AnimateDown, + Down, + AnimateUp, + } + + public float animationDuration = .1f; + public AnimationCurve animationCurve = AnimationCurve.EaseInOut(0, 0, 1, 1); + public bool useEnterExitEvents = true; + + public event Action willPress; + public event Action willRelease; + + protected State state = State.Up; + + /// + /// 0 = fully up + /// 1 = fully down + /// + protected float pressAmount = 0; + + protected bool IsAnimating => state == State.AnimateDown || state == State.AnimateUp; + + void Update() + { + PollPointerUp(); + DoAnimation(); + } + + void DoAnimation() + { + if (!IsAnimating) return; + + if (state == State.AnimateDown) + { + pressAmount += Time.deltaTime / animationDuration; + } + else if (state == State.AnimateUp) + { + pressAmount -= Time.deltaTime / animationDuration; + } + + pressAmount = Mathf.Clamp01(pressAmount); + var animationProgress = pressAmount; + if (state == State.AnimateUp) animationProgress = 1 - animationProgress; + animationProgress = animationCurve.Evaluate(animationProgress); + if (state == State.AnimateUp) animationProgress = 1 - animationProgress; + + Animate(animationProgress); + + if (state == State.AnimateDown && pressAmount == 1) + { + state = State.Down; + } + + if (state == State.AnimateUp && pressAmount == 0) + { + state = State.Up; + } + } + + protected void Press() + { + if (state != State.Down && state != State.AnimateDown) + { + OnWillPress(); + state = State.AnimateDown; + } + } + + protected void Release() + { + if (state != State.Up && state != State.AnimateUp) + { + OnWillRelease(); + state = State.AnimateUp; + } + } + + /// + /// Pointer Up event does not fire on an object if it was not the one receive the Pointer Down event. + /// + void PollPointerUp() + { + if (useEnterExitEvents + && (state == State.Down || state == State.AnimateDown) + && !Input.GetMouseButton(0)) + { + Release(); + } + } + + /// + /// NOP if not overrided + /// + /// conformed to + protected virtual void Animate(float visualPressAmount) { } + + public void OnPointerDown(PointerEventData eventData) + { + Press(); + } + + public void OnPointerUp(PointerEventData eventData) + { + Release(); + } + + public void OnPointerEnter(PointerEventData eventData) + { + if (useEnterExitEvents && Input.GetMouseButton(0)) Press(); + } + + public void OnPointerExit(PointerEventData eventData) + { + if (useEnterExitEvents) Release(); + } + + protected virtual void OnWillPress() + { + willPress?.Invoke(); + } + + protected virtual void OnWillRelease() + { + willRelease?.Invoke(); + } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/AnimatedBiStateButton.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/AnimatedBiStateButton.cs.meta new file mode 100644 index 0000000..0044b03 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/AnimatedBiStateButton.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d498001a5177468b90ceba93a17a5307 +timeCreated: 1603966347 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InsetOnPress.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InsetOnPress.cs new file mode 100644 index 0000000..cc92ff8 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InsetOnPress.cs @@ -0,0 +1,69 @@ +using UnityEngine; + +namespace LeTai.TrueShadow +{ +[RequireComponent(typeof(TrueShadow))] +public class InsetOnPress : AnimatedBiStateButton +{ + TrueShadow[] shadows; + float[] normalOpacity; + bool wasInset; + + void OnEnable() + { + shadows = GetComponents(); + normalOpacity = new float[shadows.Length]; + } + + protected override void Animate(float visualPressAmount) + { + void SetAllOpacity(float lerpProgress) + { + for (var i = 0; i < shadows.Length; i++) + { + var color = shadows[i].Color; + color.a = Mathf.Lerp(0, normalOpacity[i], lerpProgress); + shadows[i].Color = color; + } + } + + bool shouldInset = visualPressAmount > .5f; + + if (shouldInset != wasInset) + { + for (var i = 0; i < shadows.Length; i++) + { + shadows[i].Inset = shouldInset; + } + + wasInset = shouldInset; + } + + if (shouldInset) + { + SetAllOpacity(visualPressAmount * 2f - 1f); + } + else + { + SetAllOpacity(1 - visualPressAmount * 2f); + } + } + + void MemorizeOpacity() + { + if (IsAnimating) return; + + for (var i = 0; i < shadows.Length; i++) + { + normalOpacity[i] = shadows[i].Color.a; + } + } + + protected override void OnWillPress() + { + wasInset = shadows[0].Inset; + MemorizeOpacity(); + base.OnWillPress(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InsetOnPress.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InsetOnPress.cs.meta new file mode 100644 index 0000000..ea8f5a9 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InsetOnPress.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dd53a06400e8496e82467031771bf46e +timeCreated: 1602747228 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InteractiveShadow.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InteractiveShadow.cs new file mode 100644 index 0000000..c286c43 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InteractiveShadow.cs @@ -0,0 +1,242 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using static UnityEngine.Mathf; + +namespace LeTai.TrueShadow +{ +[RequireComponent(typeof(TrueShadow))] +public class InteractiveShadow : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, ISelectHandler, + IDeselectHandler, IPointerDownHandler, IPointerUpHandler +{ + public float smoothTime = .05f; + + [Tooltip("Deselect on pointer up")] + public bool autoDeselect; + + [Header("Size")] + public float selectedSize = 28; + + public float hoverSize = 28; + public float clickedSize = 24; + + [Header("Distance")] + public float selectedDistance = 12; + + public float hoverDistance = 12; + public float clickedDistance = 8; + + [Header("Color")] + public Color selectedColor = new Color(0, 0, 0, .25f); + + public Color hoverColor = new Color(0, 0, 0, .20f); + public Color clickedColor = new Color(0, 0, 0, .25f); + + float normalSize; + float normalDistance; + Color normalColor; + + bool isSelected; + bool isHovered; + bool isClicked; + + TrueShadow shadow; + Selectable selectable; + + float targetSize; + float targetDistance; + Color targetColor; + + static readonly Color FADED_COLOR = new Color(.5f, .5f, .5f, .5f); + +#if UNITY_EDITOR + void Reset() + { + shadow = FindTrueShadow(); + if (shadow) + { + normalSize = shadow.Size; + normalDistance = shadow.OffsetDistance; + normalColor = shadow.Color; + + // Clicked UI remain selected, which is unwanted. Selected state is probably most useful on console + // and keyboard nav, the later is rather hard to detect + bool selectedIsNormal = Input.mousePresent || Input.touchSupported; + autoDeselect = selectedIsNormal; + + hoverSize = Round(Min(normalSize * 1.75f, normalSize + 20f)); + selectedSize = selectedIsNormal ? normalSize : hoverSize; + clickedSize = Round(Min(normalSize * 1.25f, normalSize + 15f)); + + hoverDistance = Round(Min(normalDistance * 1.5f, normalDistance + 20f)); + selectedDistance = selectedIsNormal ? normalDistance : hoverDistance; + clickedDistance = Round(Min(normalDistance * 1.25f, normalDistance + 15f)); + + + hoverColor = Color.Lerp(normalColor, FADED_COLOR, .15f); + selectedColor = selectedIsNormal ? normalColor : hoverColor; + clickedColor = Color.Lerp(normalColor, FADED_COLOR, .25f); + } + } +#endif + + void OnEnable() + { + shadow = FindTrueShadow(); + selectable = GetComponent(); + + targetSize = normalSize = shadow.Size; + targetDistance = normalDistance = shadow.OffsetDistance; + targetColor = normalColor = shadow.Color; + + shadow.Size = targetSize = normalSize; + shadow.OffsetDistance = targetDistance = normalDistance; + } + + TrueShadow FindTrueShadow() + { + var shadows = GetComponents(); + if (shadows.Length == 0) return null; + + var ishadows = GetComponents(); + + int index = 0; + for (; index < ishadows.Length; index++) + if (ishadows[index] == this) + break; + + return shadows[index]; + } + + void OnStateChange() + { + if (isClicked) + { + targetSize = clickedSize; + targetDistance = clickedDistance; + targetColor = clickedColor; + } + else if (isSelected) + { + targetSize = selectedSize; + targetDistance = selectedDistance; + targetColor = selectedColor; + } + else if (isHovered) + { + targetSize = hoverSize; + targetDistance = hoverDistance; + targetColor = hoverColor; + } + else + { + targetSize = normalSize; + targetDistance = normalDistance; + targetColor = normalColor; + } + + + if (selectable && selectable.interactable && selectable.isActiveAndEnabled) + { + if (selectable.transition == Selectable.Transition.ColorTint) + { + StopAllCoroutines(); + StartCoroutine(DirtyForSeconds(selectable.colors.fadeDuration)); + } + } + } + + IEnumerator DirtyForSeconds(float duration) + { + var start = Time.time; + while (start + duration >= Time.time) + { + shadow.SetTextureDirty(); + yield return null; + } + } + + +#region AnimationState + + float currentSizeVelocity; + float currentDistanceVelocity; + float currentColorRVelocity; + float currentColorGVelocity; + float currentColorBVelocity; + float currentColorAVelocity; + +#endregion + + + void Update() + { + if (!Approximately(targetSize, shadow.Size)) + { + shadow.Size = SmoothDamp(shadow.Size, targetSize, + ref currentSizeVelocity, smoothTime); + } + + if (!Approximately(targetDistance, shadow.OffsetDistance)) + { + shadow.OffsetDistance = SmoothDamp(shadow.OffsetDistance, targetDistance, + ref currentDistanceVelocity, smoothTime); + } + + var shadowColor = shadow.Color; + if (!Approximately(targetColor.a, shadowColor.a)) + { + var r = SmoothDamp(shadowColor.r, targetColor.r, + ref currentColorRVelocity, smoothTime); + var g = SmoothDamp(shadowColor.g, targetColor.g, + ref currentColorGVelocity, smoothTime); + var b = SmoothDamp(shadowColor.b, targetColor.b, + ref currentColorBVelocity, smoothTime); + var a = SmoothDamp(shadowColor.a, targetColor.a, + ref currentColorAVelocity, smoothTime); + + shadow.Color = new Color(r, g, b, a); + } + } + + public void OnPointerEnter(PointerEventData eventData) + { + isHovered = true; + OnStateChange(); + } + + public void OnPointerExit(PointerEventData eventData) + { + isHovered = false; + OnStateChange(); + } + + public void OnSelect(BaseEventData eventData) + { + isSelected = true; + OnStateChange(); + } + + public void OnDeselect(BaseEventData eventData) + { + isSelected = false; + OnStateChange(); + } + + public void OnPointerDown(PointerEventData eventData) + { + isClicked = true; + OnStateChange(); + } + + public void OnPointerUp(PointerEventData eventData) + { + if (autoDeselect && EventSystem.current.currentSelectedGameObject == gameObject) + EventSystem.current.SetSelectedGameObject(null); + + isClicked = false; + OnStateChange(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InteractiveShadow.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InteractiveShadow.cs.meta new file mode 100644 index 0000000..ff4f192 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Helper/InteractiveShadow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2b6bd66378f54ffc974312b194733dd9 +timeCreated: 1593508708 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/PluginInterfaces.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/PluginInterfaces.cs new file mode 100644 index 0000000..3576c99 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/PluginInterfaces.cs @@ -0,0 +1,40 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace LeTai.TrueShadow.PluginInterfaces +{ +public interface ITrueShadowCasterMaterialProvider +{ + Material GetTrueShadowCasterMaterial(); +} + +public interface ITrueShadowCasterMeshModifier +{ + void ModifyTrueShadowCasterMesh(Mesh mesh); +} + +public interface ITrueShadowCasterMaterialPropertiesModifier +{ + void ModifyTrueShadowCasterMaterialProperties(MaterialPropertyBlock propertyBlock); +} + +public interface ITrueShadowCasterClearColorProvider +{ + Color GetTrueShadowCasterClearColor(); +} + +public interface ITrueShadowRendererMaterialProvider +{ + Material GetTrueShadowRendererMaterial(); +} + +public interface ITrueShadowRendererMaterialModifier +{ + void ModifyTrueShadowRendererMaterial(Material baseMaterial); +} + +public interface ITrueShadowRendererMeshModifier +{ + void ModifyTrueShadowRenderMesh(VertexHelper vertexHelper); +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/PluginInterfaces.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/PluginInterfaces.cs.meta new file mode 100644 index 0000000..49f3031 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/PluginInterfaces.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 81f076a2e89b8b34da77ca09f22cfe59 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowFactory.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowFactory.cs new file mode 100644 index 0000000..46dce7a --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowFactory.cs @@ -0,0 +1,312 @@ +using System.Collections.Generic; +using LeTai.Effects; +using UnityEngine; +using UnityEngine.Rendering; + +namespace LeTai.TrueShadow +{ +public class ShadowFactory +{ + private static ShadowFactory instance; + public static ShadowFactory Instance => instance ?? (instance = new ShadowFactory()); + + readonly Dictionary shadowCache = + new Dictionary(); + + readonly CommandBuffer cmd; + readonly MaterialPropertyBlock materialProps; + readonly ScalableBlur blurProcessor; + readonly ScalableBlurConfig blurConfig; + + Material cutoutMaterial; + Material imprintPostProcessMaterial; + Material shadowPostProcessMaterial; + + Material CutoutMaterial => + cutoutMaterial ? cutoutMaterial : cutoutMaterial = new Material(Shader.Find("Hidden/TrueShadow/Cutout")); + + Material ImprintPostProcessMaterial => + imprintPostProcessMaterial + ? imprintPostProcessMaterial + : imprintPostProcessMaterial = new Material(Shader.Find("Hidden/TrueShadow/ImprintPostProcess")); + + Material ShadowPostProcessMaterial => + shadowPostProcessMaterial + ? shadowPostProcessMaterial + : shadowPostProcessMaterial = new Material(Shader.Find("Hidden/TrueShadow/PostProcess")); + + private ShadowFactory() + { + cmd = new CommandBuffer {name = "Shadow Commands"}; + materialProps = new MaterialPropertyBlock(); + materialProps.SetVector(ShaderId.CLIP_RECT, + new Vector4(float.NegativeInfinity, float.NegativeInfinity, + float.PositiveInfinity, float.PositiveInfinity)); + materialProps.SetInt(ShaderId.COLOR_MASK, (int) ColorWriteMask.All); // Render shadow even if mask hide graphic + + ShaderProperties.Init(8); + blurConfig = ScriptableObject.CreateInstance(); + blurConfig.hideFlags = HideFlags.HideAndDontSave; + blurProcessor = new ScalableBlur(); + blurProcessor.Configure(blurConfig); + } + + ~ShadowFactory() + { + cmd.Dispose(); + Utility.SafeDestroy(blurConfig); + Utility.SafeDestroy(cutoutMaterial); + Utility.SafeDestroy(imprintPostProcessMaterial); + } + +#if LETAI_TRUESHADOW_DEBUG + RenderTexture debugTexture; +#endif + + // public int createdContainerCount; + // public int releasedContainerCount; + + internal void Get(ShadowSettingSnapshot snapshot, ref ShadowContainer container) + { + if (float.IsNaN(snapshot.dimensions.x) || snapshot.dimensions.x < 1 || + float.IsNaN(snapshot.dimensions.y) || snapshot.dimensions.y < 1) + { + ReleaseContainer(container); + return; + } + +#if LETAI_TRUESHADOW_DEBUG + RenderTexture.ReleaseTemporary(debugTexture); + if (snapshot.shadow.alwaysRender) + debugTexture = GenerateShadow(snapshot).Texture; +#endif + + // Each request need a coresponding shadow texture + // Texture may be shared by multiple elements + // Texture are released when no longer used by any element + // ShadowContainer keep track of texture and their usage + + + int requestHash = snapshot.GetHashCode(); + + // Case: requester can keep the same texture + if (container?.requestHash == requestHash) + return; + + ReleaseContainer(container); + + if (shadowCache.TryGetValue(requestHash, out var existingContainer)) + { + // Case: requester got texture from someone else + existingContainer.RefCount++; + container = existingContainer; + } + else + { + // Case: requester got new unique texture + container = shadowCache[requestHash] = GenerateShadow(snapshot); + // Debug.Log($"Created new container for request\t{requestHash}\tTotal Created: {++createdContainerCount}\t Alive: {createdContainerCount - releasedContainerCount}"); + } + } + + internal void ReleaseContainer(ShadowContainer container) + { + if (container == null) + return; + + if (--container.RefCount > 0) + return; + + RenderTexture.ReleaseTemporary(container.Texture); + shadowCache.Remove(container.requestHash); + + // Debug.Log($"Released container for request\t{container.requestHash}\tTotal Released: {++releasedContainerCount}\t Alive: {createdContainerCount - releasedContainerCount}"); + } + + static readonly Rect UNIT_RECT = new Rect(0, 0, 1, 1); + + ShadowContainer GenerateShadow(ShadowSettingSnapshot snapshot) + { + // return GenColoredTexture(request.GetHashCode()); + + cmd.Clear(); + cmd.BeginSample("TrueShadow:Capture"); + + var bounds = snapshot.shadow.SpriteMesh.bounds; + var misalignment = CalcMisalignment(snapshot.canvas, snapshot.canvasRt, snapshot.shadow.RectTransform, bounds); + + var padding = Mathf.CeilToInt(snapshot.size); + var imprintViewW = Mathf.RoundToInt(snapshot.dimensions.x + misalignment.bothSS.x); + var imprintViewH = Mathf.RoundToInt(snapshot.dimensions.y + misalignment.bothSS.y); + var tw = imprintViewW + padding * 2; + var th = imprintViewH + padding * 2; + + var shadowTex = RenderTexture.GetTemporary(tw, th, 0, RenderTextureFormat.ARGB32); + var imprintTexDesc = shadowTex.descriptor; + imprintTexDesc.msaaSamples = snapshot.shouldAntialiasImprint ? Mathf.Max(1, QualitySettings.antiAliasing) : 1; + var imprintTex = RenderTexture.GetTemporary(imprintTexDesc); + + RenderTexture imprintTexProcessed = null; + + bool needProcessImprint = snapshot.shadow.IgnoreCasterColor || snapshot.shadow.Inset; + if (needProcessImprint) + imprintTexProcessed = RenderTexture.GetTemporary(imprintTexDesc); + + var texture = snapshot.shadow.Content; + if (texture) + materialProps.SetTexture(ShaderId.MAIN_TEX, texture); + else + materialProps.SetTexture(ShaderId.MAIN_TEX, Texture2D.whiteTexture); + + cmd.SetRenderTarget(imprintTex); + cmd.ClearRenderTarget(true, true, snapshot.shadow.ClearColor); + + cmd.SetViewport(new Rect(padding, padding, imprintViewW, imprintViewH)); + + var imprintBoundMin = (Vector2) bounds.min - misalignment.minLS; + var imprintBoundMax = (Vector2) bounds.max + misalignment.maxLS; + cmd.SetViewProjectionMatrices( + Matrix4x4.identity, + Matrix4x4.Ortho(imprintBoundMin.x, imprintBoundMax.x, + imprintBoundMin.y, imprintBoundMax.y, + -1, 1) + ); + + snapshot.shadow.ModifyShadowCastingMesh(snapshot.shadow.SpriteMesh); + snapshot.shadow.ModifyShadowCastingMaterialProperties(materialProps); + cmd.DrawMesh(snapshot.shadow.SpriteMesh, + Matrix4x4.identity, + snapshot.shadow.GetShadowCastingMaterial(), + 0, 0, + materialProps); + + if (needProcessImprint) + { + ImprintPostProcessMaterial.SetKeyword("BLEACH", snapshot.shadow.IgnoreCasterColor); + ImprintPostProcessMaterial.SetKeyword("INSET", snapshot.shadow.Inset); + + cmd.Blit(imprintTex, imprintTexProcessed, ImprintPostProcessMaterial); + } + + cmd.EndSample("TrueShadow:Capture"); + + var needPostProcess = snapshot.shadow.Spread > 1e-3; + + cmd.BeginSample("TrueShadow:Cast"); + RenderTexture blurSrc = needProcessImprint ? imprintTexProcessed : imprintTex; + RenderTexture blurDst; + if (needPostProcess) + blurDst = RenderTexture.GetTemporary(shadowTex.descriptor); + else + blurDst = shadowTex; + + if (snapshot.size < 1e-2) + { + cmd.Blit(blurSrc, blurDst); + } + else + { + blurConfig.Strength = snapshot.size; + blurProcessor.Blur(cmd, blurSrc, UNIT_RECT, blurDst); + } + + cmd.EndSample("TrueShadow:Cast"); + + var relativeOffset = new Vector2(snapshot.canvasRelativeOffset.x / tw, + snapshot.canvasRelativeOffset.y / th); + var overflowAlpha = snapshot.shadow.Inset ? 1 : 0; + if (needPostProcess) + { + cmd.BeginSample("TrueShadow:PostProcess"); + + ShadowPostProcessMaterial.SetTexture(ShaderId.SHADOW_TEX, blurDst); + ShadowPostProcessMaterial.SetVector(ShaderId.OFFSET, relativeOffset); + ShadowPostProcessMaterial.SetFloat(ShaderId.OVERFLOW_ALPHA, overflowAlpha); + ShadowPostProcessMaterial.SetFloat(ShaderId.ALPHA_MULTIPLIER, + 1f / Mathf.Max(1e-6f, 1f - snapshot.shadow.Spread)); + + cmd.SetViewport(UNIT_RECT); + cmd.Blit(blurSrc, shadowTex, ShadowPostProcessMaterial); + + cmd.EndSample("TrueShadow:PostProcess"); + } + else if (snapshot.shadow.Cutout) + { + cmd.BeginSample("TrueShadow:Cutout"); + + CutoutMaterial.SetVector(ShaderId.OFFSET, relativeOffset); + CutoutMaterial.SetFloat(ShaderId.OVERFLOW_ALPHA, overflowAlpha); + + cmd.SetViewport(UNIT_RECT); + cmd.Blit(blurSrc, shadowTex, CutoutMaterial); + + cmd.EndSample("TrueShadow:Cutout"); + } + + Graphics.ExecuteCommandBuffer(cmd); + + RenderTexture.ReleaseTemporary(imprintTex); + RenderTexture.ReleaseTemporary(blurSrc); + if (needPostProcess) + RenderTexture.ReleaseTemporary(blurDst); + + return new ShadowContainer(shadowTex, snapshot, padding, misalignment.minLS); + } + + readonly struct PixelMisalignment + { + public readonly Vector2 bothSS; + public readonly Vector2 minLS; + public readonly Vector2 maxLS; + + public PixelMisalignment(Vector2 bothSS, Vector2 minLS, Vector2 maxLS) + { + this.bothSS = bothSS; + this.minLS = minLS; + this.maxLS = maxLS; + } + } + + PixelMisalignment CalcMisalignment(Canvas canvas, RectTransform canvasRt, RectTransform casterRt, Bounds meshBound) + { + PixelMisalignment misalignment; + + if (canvas.renderMode == RenderMode.WorldSpace) + { + misalignment = new PixelMisalignment(); + } + else + { + var referenceCamera = canvas.renderMode == RenderMode.ScreenSpaceCamera ? canvas.worldCamera : null; + + var pxMisalignmentAtMin = casterRt.LocalToScreenPoint(meshBound.min, referenceCamera).Frac(); + var pxMisalignmentAtMax = + Vector2.one - casterRt.LocalToScreenPoint(meshBound.max, referenceCamera).Frac(); + if (pxMisalignmentAtMax.x > 1 - 1e-5) + pxMisalignmentAtMax.x = 0; + if (pxMisalignmentAtMax.y > 1 - 1e-5) + pxMisalignmentAtMax.y = 0; + + misalignment = new PixelMisalignment( + pxMisalignmentAtMin + pxMisalignmentAtMax, + canvasRt.ScreenToCanvasSize(pxMisalignmentAtMin, referenceCamera), + canvasRt.ScreenToCanvasSize(pxMisalignmentAtMax, referenceCamera) + ); + } + + return misalignment; + } + + RenderTexture GenColoredTexture(int hash) + { + var tex = new Texture2D(1, 1); + tex.SetPixels32(new[] {new Color32((byte) (hash >> 8), (byte) (hash >> 16), (byte) (hash >> 24), 255)}); + tex.Apply(); + + var rt = RenderTexture.GetTemporary(1, 1); + Graphics.Blit(tex, rt); + + return rt; + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowFactory.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowFactory.cs.meta new file mode 100644 index 0000000..6af0ab0 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8d1278f371c48a784a5294591372531 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.MaskHandling.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.MaskHandling.cs new file mode 100644 index 0000000..bc07dfe --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.MaskHandling.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.UI; + +namespace LeTai.TrueShadow +{ +public partial class ShadowRenderer +{ + // TODO: cleanup unused mask materials + static readonly Dictionary<(bool, Material), Material> MASK_MATERIALS_CACHE = + new Dictionary<(bool, Material), Material>(); + + internal static void ClearMaskMaterialCache() + { + foreach (var keyValuePair in MASK_MATERIALS_CACHE) + { + if(Application.isPlaying) + Destroy(keyValuePair.Value); + else + DestroyImmediate(keyValuePair.Value); + } + + MASK_MATERIALS_CACHE.Clear(); + } + + public Material GetModifiedMaterial(Material baseMaterial) + { + if (!shadow) + return baseMaterial; + + shadow.ModifyShadowRendererMaterial(baseMaterial); + + if (!baseMaterial.HasProperty(ShaderId.COLOR_MASK) || + !baseMaterial.HasProperty(ShaderId.STENCIL_OP)) + return baseMaterial; + + bool casterIsMask = shadow.GetComponent() != null; + + MASK_MATERIALS_CACHE.TryGetValue((casterIsMask, baseMaterial), out var mat); + + if (!mat) + { + mat = new Material(baseMaterial); + + if (shadow.ShadowAsSibling) + { + // Prevent shadow from writing to stencil mask + mat.SetInt(ShaderId.COLOR_MASK, (int) ColorWriteMask.All); + mat.SetInt(ShaderId.STENCIL_OP, (int) StencilOp.Keep); + } + else if (casterIsMask) + { + // Escape mask if we have one + var baseStencilId = mat.GetInt(ShaderId.STENCIL_ID) + 1; + int stencilDepth = 0; + for (; stencilDepth < 8; stencilDepth++) + { + if (((baseStencilId >> stencilDepth) & 1) == 1) + break; + } + + stencilDepth = Mathf.Max(0, stencilDepth - 1); + var stencilId = (1 << stencilDepth) - 1; + + mat.SetInt(ShaderId.STENCIL_ID, stencilId); + mat.SetInt(ShaderId.STENCIL_READ_MASK, stencilId); + } + + MASK_MATERIALS_CACHE[(casterIsMask, baseMaterial)] = mat; + } + + return mat; + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.MaskHandling.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.MaskHandling.cs.meta new file mode 100644 index 0000000..fa4222d --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.MaskHandling.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b035d21af0d3447296a96ea4f27b5f53 +timeCreated: 1600848777 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.cs new file mode 100644 index 0000000..39b968a --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.cs @@ -0,0 +1,189 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace LeTai.TrueShadow +{ +[AddComponentMenu("")] +[ExecuteAlways] +public partial class ShadowRenderer : MonoBehaviour, ILayoutIgnorer, IMaterialModifier, IMeshModifier +{ + public bool ignoreLayout => true; + + internal CanvasRenderer CanvasRenderer { get; private set; } + + TrueShadow shadow; + RectTransform rt; + RawImage graphic; + Texture shadowTexture; + + public static void Initialize(TrueShadow shadow, ref ShadowRenderer renderer) + { + if (renderer && renderer.shadow == shadow) + { + renderer.gameObject.SetActive(true); + return; + } + + var obj = new GameObject($"{shadow.gameObject.name}'s Shadow") { +#if LETAI_TRUESHADOW_DEBUG + hideFlags = DebugSettings.Instance.showObjects + ? HideFlags.DontSave + : HideFlags.HideAndDontSave +#else + hideFlags = HideFlags.HideAndDontSave +#endif + }; + +#if LETAI_TRUESHADOW_DEBUG && UNITY_EDITOR + UnityEditor.SceneVisibilityManager.instance.DisablePicking(obj, true); +#endif + + shadow.SetHierachyDirty(); + + var rt = obj.AddComponent(); + rt.anchorMin = Vector2.zero; + rt.anchorMax = Vector2.zero; + + var graphic = obj.AddComponent(); + graphic.raycastTarget = false; + graphic.color = shadow.Color; + + renderer = obj.AddComponent(); + renderer.shadow = shadow; + renderer.rt = rt; + renderer.graphic = graphic; + + // renderer.RecreateGraphic(shadow.Baked ? GraphicType.Image : GraphicType.RawImage); + + renderer.UpdateMaterial(); + + renderer.CanvasRenderer = obj.GetComponent(); + renderer.CanvasRenderer.SetColor(shadow.IgnoreCasterColor ? Color.white : shadow.CanvasRenderer.GetColor()); + renderer.CanvasRenderer.SetAlpha(shadow.CanvasRenderer.GetAlpha()); + + renderer.ReLayout(); + } + + public void UpdateMaterial() + { + var mat = shadow.BlendMode.GetMaterial(); + graphic.material = mat ? mat : shadow.GetShadowRenderingNormalMaterial(); + } + + internal void ReLayout() + { + if (!isActiveAndEnabled) + return; + + var casterRt = shadow.RectTransform; + if (!casterRt) + { + CanvasRenderer.SetAlpha(0); + return; + } + + if (!shadowTexture) + { + CanvasRenderer.SetAlpha(0); + return; + } + + var container = shadow.ShadowContainer; + var canvasScale = container?.Snapshot?.canvasScale ?? graphic.canvas.scaleFactor; + + var shadowTexSize = new Vector2(shadowTexture.width, shadowTexture.height) / canvasScale; + rt.sizeDelta = shadowTexSize; + + // pivot should be relative to the un-blurred part of the texture, not the whole mesh + var casterPivotLS = -(Vector2) shadow.SpriteMesh.bounds.min; + var padding = (container?.Padding ?? Mathf.CeilToInt(shadow.Size * canvasScale)) / canvasScale * Vector2.one; + var misalign = container?.PxMisalignmentAtMinLS ?? Vector2.zero; + rt.pivot = (casterPivotLS + padding + misalign) / shadowTexSize; + + + var canvasRelativeOffset = container?.Snapshot?.canvasRelativeOffset / canvasScale ?? shadow.Offset; + var offset = shadow.ShadowAsSibling + ? shadow.Offset.WithZ(0) + : canvasRelativeOffset.WithZ(0); + rt.localPosition = shadow.ShadowAsSibling + ? casterRt.localPosition + offset + : offset; + + rt.localRotation = shadow.ShadowAsSibling ? casterRt.localRotation : Quaternion.identity; + rt.localScale = shadow.ShadowAsSibling ? casterRt.localScale : Vector3.one; + + + var color = shadow.Color; + if (shadow.UseCasterAlpha) + color.a *= shadow.Graphic.color.a; + graphic.color = color; + + CanvasRenderer.SetColor(shadow.IgnoreCasterColor ? Color.white : shadow.CanvasRenderer.GetColor()); + CanvasRenderer.SetAlpha(shadow.CanvasRenderer.GetAlpha()); + + graphic.Rebuild(CanvasUpdate.PreRender); + } + + public void SetTexture(Texture texture) + { + shadowTexture = texture; + CanvasRenderer.SetTexture(texture); + graphic.texture = texture; + } + + public void SetMaterialDirty() + { + graphic.SetMaterialDirty(); + } + + public void ModifyMesh(VertexHelper vertexHelper) + { + if (!shadow) + return; + + shadow.ModifyShadowRendererMesh(vertexHelper); + } + + public void ModifyMesh(Mesh mesh) + { + Debug.Assert(true, "This should only be called on old unsupported Unity version"); + } + + protected virtual void LateUpdate() + { + // Destroy events are not consistently called for some reason, have to poll + if (!shadow) + Dispose(); + } + + bool willBeDestroyed; + + protected virtual void OnDestroy() + { + willBeDestroyed = true; + } + + public void Dispose() + { + if (willBeDestroyed) return; + + if (shadow && shadow.ShadowAsSibling) + { + // Destroy does not happen immediately. Want out of hierarchy. + gameObject.SetActive(false); + transform.SetParent(null); + } + +#if UNITY_EDITOR + // This look redundant but is necessary! + if (!Application.isPlaying && !UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) + DestroyImmediate(gameObject); + else if (Application.isPlaying) + Destroy(gameObject); +#else + Destroy(gameObject); +#endif + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.cs.meta new file mode 100644 index 0000000..a16fa95 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowRenderer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8c213e24c3734f43b893d039b31ac5bc +timeCreated: 1592813121 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSettingSnapshot.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSettingSnapshot.cs new file mode 100644 index 0000000..85cd037 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSettingSnapshot.cs @@ -0,0 +1,139 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace LeTai.TrueShadow +{ +class ShadowSettingSnapshot +{ + public readonly TrueShadow shadow; + public readonly Canvas canvas; + public readonly RectTransform canvasRt; + public readonly bool shouldAntialiasImprint; + public readonly float canvasScale; + public readonly float size; + public readonly Vector2 canvasRelativeOffset; + public readonly Vector2 dimensions; + + internal ShadowSettingSnapshot(TrueShadow shadow) + { + this.shadow = shadow; + canvas = shadow.Graphic.canvas; + canvasRt = (RectTransform) canvas.transform; + + var meshBound = shadow.SpriteMesh.bounds; + + shouldAntialiasImprint = canvas.renderMode != RenderMode.ScreenSpaceOverlay; + + canvasScale = canvas.scaleFactor; + + var canvasRelativeRotation = Quaternion.Inverse(canvasRt.rotation) * shadow.RectTransform.rotation; + canvasRelativeOffset = shadow.Offset.Rotate(-canvasRelativeRotation.eulerAngles.z) * canvasScale; + + dimensions = (Vector2) meshBound.size * canvasScale; + size = shadow.Size * canvasScale; + + CalcHash(); + } + + const int DIMENSIONS_HASH_STEP = 1; + + void CalcHash() + { + var graphic = shadow.Graphic; + + int canvasScaleHash = (int) (canvasScale * 1e4); + int insetHash = shadow.Inset ? 1 : 0; + + var clearColor = shadow.ClearColor; + var imageColor = graphic.color; + int colorHash = HashUtils.CombineHashCodes( + shadow.IgnoreCasterColor ? 1 : 0, + (int) shadow.ColorBleedMode, + (int) (imageColor.r * 255), + (int) (imageColor.g * 255), + (int) (imageColor.b * 255), + (int) (imageColor.a * 255), + (int) (clearColor.r * 255), + (int) (clearColor.g * 255), + (int) (clearColor.b * 255), + (int) (clearColor.a * 255) + ); + + // Hack until we have separated cutout cache, or proper sibling mode + int offsetHash = HashUtils.CombineHashCodes( + shadow.Cutout ? 1 : 0, + (int) (canvasRelativeOffset.x * 100), + (int) (canvasRelativeOffset.y * 100) + ); + + // Tiled type cannot be batched by similar size + int dimensionHash = graphic is Image im && im.type == Image.Type.Tiled + ? dimensions.GetHashCode() + : HashUtils.CombineHashCodes( + Mathf.CeilToInt(dimensions.x / DIMENSIONS_HASH_STEP) * DIMENSIONS_HASH_STEP, + Mathf.CeilToInt(dimensions.y / DIMENSIONS_HASH_STEP) * DIMENSIONS_HASH_STEP + ); + + var sizeHash = Mathf.CeilToInt(size * 100); + + var commonHash = HashUtils.CombineHashCodes( + shadow.TextureRevision, + canvasScaleHash, + insetHash, + colorHash, + offsetHash, + dimensionHash, + sizeHash + ); + + switch (graphic) + { + case Image image: + int spriteHash = 0; + if (image.sprite) + spriteHash = image.sprite.GetHashCode(); + + int imageHash = HashUtils.CombineHashCodes( + (int) image.type, + (int) (image.fillAmount * 360 * 20), + (int) image.fillMethod, + image.fillOrigin, + image.fillClockwise ? 1 : 0 + ); + + hash = HashUtils.CombineHashCodes( + commonHash, + spriteHash, + imageHash + ); + break; + case RawImage rawImage: + var textureHash = 0; + if (rawImage.texture) + textureHash = rawImage.texture.GetInstanceID(); + + hash = HashUtils.CombineHashCodes( + commonHash, + textureHash + ); + break; + default: + hash = commonHash; + break; + } + } + + int hash; + + // ReSharper disable once NonReadonlyMemberInGetHashCode + public override int GetHashCode() => hash; + + public override bool Equals(object obj) + { + if (obj == null) return false; + + return GetHashCode() == obj.GetHashCode(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSettingSnapshot.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSettingSnapshot.cs.meta new file mode 100644 index 0000000..1fb2950 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSettingSnapshot.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 93693e4b8ffcbca48bc39fba2e37ffda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSorter.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSorter.cs new file mode 100644 index 0000000..06387db --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSorter.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace LeTai.TrueShadow +{ +[ExecuteAlways] +public class ShadowSorter : MonoBehaviour +{ +#region SortDataContainer + readonly struct SortEntry : IComparable + { + public readonly TrueShadow shadow; + public readonly Transform shadowTransform; + public readonly Transform rendererTransform; + + public SortEntry(TrueShadow shadow) + { + this.shadow = shadow; + shadowTransform = shadow.transform; + rendererTransform = shadow.shadowRenderer.transform; + } + + public int CompareTo(SortEntry other) + { + return other.shadowTransform.GetSiblingIndex().CompareTo(shadowTransform.GetSiblingIndex()); + } + } + + readonly struct SortGroup + { + public readonly Transform parentTransform; + public readonly List sortEntries; + + public SortGroup(SortEntry firstEntry) + { + sortEntries = new List {firstEntry}; + parentTransform = firstEntry.shadowTransform.parent; + } + + public void Add(SortEntry pair) + { + if (pair.shadowTransform.parent != parentTransform) + return; + + var index = sortEntries.BinarySearch(pair); + if (index < 0) + sortEntries.Insert(~index, pair); + } + + public override int GetHashCode() + { + return parentTransform.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is SortGroup other && other.parentTransform == parentTransform; + } + } +#endregion + + + private static ShadowSorter instance; + + public static ShadowSorter Instance + { + get + { + if (!instance) + { + var existings = FindObjectsOfType(); + for (int i = existings.Length - 1; i > 0; i--) + { + Destroy(existings[i]); + } + +#if UNITY_EDITOR + var hidden = GameObject.Find("/" + nameof(ShadowSorter)); + while (hidden) + { + DestroyImmediate(hidden); + hidden = GameObject.Find("/" + nameof(ShadowSorter)); + } +#endif + + instance = existings.Length > 0 ? existings[0] : null; + + if (!instance) + { + var obj = new GameObject(nameof(ShadowSorter)) { +#if LETAI_TRUESHADOW_DEBUG + hideFlags = DebugSettings.Instance.showObjects + ? HideFlags.DontSave + : HideFlags.HideAndDontSave +#else + hideFlags = HideFlags.HideAndDontSave +#endif + }; + instance = obj.AddComponent(); + } + } + + return instance; + } + } + + readonly IndexedSet shadows = new IndexedSet(); + readonly IndexedSet sortGroups = new IndexedSet(); + + public void Register(TrueShadow shadow) + { + shadows.AddUnique(shadow); + } + + public void UnRegister(TrueShadow shadow) + { + shadows.Remove(shadow); + } + + void LateUpdate() + { + if (!this) return; + + for (var i = 0; i < shadows.Count; i++) + { + var shadow = shadows[i]; + + if (!shadow || !shadow.isActiveAndEnabled) + continue; + + shadow.CheckHierarchyDirtied(); + if (shadow.hierachyDirty) + AddSortEntry(shadow); + } + + Sort(); + } + + void AddSortEntry(TrueShadow shadow) + { + var entry = new SortEntry(shadow); + var group = new SortGroup(entry); + var oldIndex = sortGroups.IndexOf(group); + if (oldIndex > -1) + sortGroups[oldIndex].Add(entry); + else + sortGroups.Add(group); + } + + public void Sort() + { + for (var i = 0; i < sortGroups.Count; i++) + { + var group = sortGroups[i]; + + if (!group.parentTransform) + continue; + + foreach (var entry in group.sortEntries) + { + entry.rendererTransform.SetParent(group.parentTransform, false); + var rendererSid = entry.rendererTransform.GetSiblingIndex(); + var shadowSid = entry.shadowTransform.GetSiblingIndex(); + if (rendererSid > shadowSid) + { + entry.rendererTransform.SetSiblingIndex(shadowSid); + } + else + { + entry.rendererTransform.SetSiblingIndex(shadowSid - 1); + } + + entry.shadow.UnSetHierachyDirty(); + } + + // This is a separated loop, as siblind index of an entry will be affected by the laters + foreach (var entry in group.sortEntries) + { + entry.shadow.ForgetSiblingIndexChanges(); + } + } + + sortGroups.Clear(); + } + + void OnApplicationQuit() + { + // make sure object are recreated when exit play mode. Otherwise it turn into some weird state. need more research + Destroy(gameObject); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSorter.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSorter.cs.meta new file mode 100644 index 0000000..7d1f482 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/ShadowSorter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cbf4f3c805fa455abb21fbb9ad6e54d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure.meta new file mode 100644 index 0000000..fa4e013 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f395e653641dd7442bba503cb28b07a0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/BlendMode.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/BlendMode.cs new file mode 100644 index 0000000..c970ca2 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/BlendMode.cs @@ -0,0 +1,42 @@ +using System; +using UnityEngine; + +namespace LeTai.TrueShadow +{ +public enum BlendMode +{ + Normal, + Additive, + Screen, + Multiply, +} + +public static class BlendModeExtensions +{ + static Material matNormal; + static Material materialAdditive; + static Material matScreen; + static Material matMultiply; + + public static Material GetMaterial(this BlendMode blendMode) + { + switch (blendMode) + { + case BlendMode.Normal: + if (!matNormal) matNormal = new Material(Shader.Find("UI/TrueShadow-Normal")); + return matNormal; + case BlendMode.Additive: + if (!materialAdditive) materialAdditive = new Material(Shader.Find("UI/TrueShadow-Additive")); + return materialAdditive; + case BlendMode.Screen: + if (!matScreen) matScreen = new Material(Shader.Find("UI/TrueShadow-Screen")); + return matScreen; + case BlendMode.Multiply: + if (!matMultiply) matMultiply = new Material(Shader.Find("UI/TrueShadow-Multiply")); + return matMultiply; + default: + throw new ArgumentOutOfRangeException(); + } + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/BlendMode.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/BlendMode.cs.meta new file mode 100644 index 0000000..da65942 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/BlendMode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3379d45961064912b9e67abda88f84da +timeCreated: 1594095942 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ColorBleedMode.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ColorBleedMode.cs new file mode 100644 index 0000000..e5fe168 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ColorBleedMode.cs @@ -0,0 +1,11 @@ +namespace LeTai.TrueShadow +{ +public enum ColorBleedMode +{ + ImageColor, + ShadowColor, + Black, + White, + Plugin +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ColorBleedMode.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ColorBleedMode.cs.meta new file mode 100644 index 0000000..482339f --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ColorBleedMode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e19e27572a184315b94a65c4dcbc9d98 +timeCreated: 1596027283 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ShadowContainer.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ShadowContainer.cs new file mode 100644 index 0000000..8a4254e --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ShadowContainer.cs @@ -0,0 +1,29 @@ +using UnityEngine; + +namespace LeTai.TrueShadow +{ +class ShadowContainer +{ + public RenderTexture Texture { get; } + public ShadowSettingSnapshot Snapshot { get; } + public int Padding { get; } + public Vector2 PxMisalignmentAtMinLS { get; } + + public int RefCount { get; internal set; } + + public readonly int requestHash; + + internal ShadowContainer(RenderTexture texture, + ShadowSettingSnapshot snapshot, + int padding, + Vector2 pxMisalignmentAtMinLS) + { + Texture = texture; + Snapshot = snapshot; + Padding = padding; + PxMisalignmentAtMinLS = pxMisalignmentAtMinLS; + RefCount = 1; + requestHash = snapshot.GetHashCode(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ShadowContainer.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ShadowContainer.cs.meta new file mode 100644 index 0000000..57503c1 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Structure/ShadowContainer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: de9d78182ae045cfad04addb713cf2e0 +timeCreated: 1595408371 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Invalidator.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Invalidator.cs new file mode 100644 index 0000000..ae5b58c --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Invalidator.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace LeTai.TrueShadow +{ +interface IChangeTracker +{ + void Check(); +} + +class ChangeTracker : IChangeTracker +{ + T previousValue; + readonly Func getValue; + readonly Func onChange; + readonly IEqualityComparer comparer; + + public ChangeTracker(Func getValue, + Func onChange, + IEqualityComparer comparer = null) + { + this.getValue = getValue; + this.onChange = onChange; + this.comparer = comparer ?? EqualityComparer.Default; + + previousValue = this.getValue(); + } + + public void Forget() + { + previousValue = getValue(); + } + + public void Check() + { + T newValue = getValue(); + if (!comparer.Equals(newValue, previousValue)) + { + previousValue = onChange(newValue); + } + } +} + +public partial class TrueShadow +{ + Action checkHierarchyDirtiedDelegate; + IChangeTracker[] transformTrackers; + ChangeTracker[] hierachyTrackers; + + void InitInvalidator() + { + checkHierarchyDirtiedDelegate = CheckHierarchyDirtied; + hierachyTrackers = new[] { + new ChangeTracker( + () => transform.GetSiblingIndex(), + newValue => + { + SetHierachyDirty(); + return newValue; // + 1; + } + ), + new ChangeTracker( + () => + { + if (shadowRenderer) + return shadowRenderer.transform.GetSiblingIndex(); + return -1; + }, + newValue => + { + SetHierachyDirty(); + return newValue; // + 1; + } + ) + }; + + transformTrackers = new IChangeTracker[] { + new ChangeTracker( + () => transform.position, + newValue => + { + SetLayoutDirty(); + return newValue; + } + ), + new ChangeTracker( + () => transform.rotation, + newValue => + { + SetLayoutDirty(); + if (Cutout) + SetTextureDirty(); + return newValue; + } + ), + }; + + Graphic.RegisterDirtyLayoutCallback(SetLayoutTextureDirty); + Graphic.RegisterDirtyVerticesCallback(SetLayoutTextureDirty); + Graphic.RegisterDirtyMaterialCallback(OnGraphicMaterialDirty); + + CheckHierarchyDirtied(); + CheckTransformDirtied(); + } + + void TerminateInvalidator() + { + if (Graphic) + { + Graphic.UnregisterDirtyLayoutCallback(SetLayoutTextureDirty); + Graphic.UnregisterDirtyVerticesCallback(SetLayoutTextureDirty); + Graphic.UnregisterDirtyMaterialCallback(OnGraphicMaterialDirty); + } + } + + void OnGraphicMaterialDirty() + { + SetLayoutTextureDirty(); + shadowRenderer.UpdateMaterial(); + } + + internal void CheckTransformDirtied() + { + if (transformTrackers != null) + { + for (var i = 0; i < transformTrackers.Length; i++) + { + transformTrackers[i].Check(); + } + } + } + + internal void CheckHierarchyDirtied() + { + if (ShadowAsSibling && hierachyTrackers != null) + { + for (var i = 0; i < hierachyTrackers.Length; i++) + { + hierachyTrackers[i].Check(); + } + } + } + + internal void ForgetSiblingIndexChanges() + { + for (var i = 0; i < hierachyTrackers.Length; i++) + { + hierachyTrackers[i].Forget(); + } + } + + protected override void OnTransformParentChanged() + { + base.OnTransformParentChanged(); + + if (!isActiveAndEnabled) return; + + SetHierachyDirty(); + this.NextFrames(checkHierarchyDirtiedDelegate); + } + + protected override void OnRectTransformDimensionsChange() + { + base.OnRectTransformDimensionsChange(); + + if (!isActiveAndEnabled) return; + + SetLayoutTextureDirty(); + } + + + protected override void OnDidApplyAnimationProperties() + { + if (!isActiveAndEnabled) return; + + SetLayoutTextureDirty(); + } + + public void ModifyMesh(Mesh mesh) + { + if (!isActiveAndEnabled) return; + + if (SpriteMesh) Utility.SafeDestroy(SpriteMesh); + SpriteMesh = Instantiate(mesh); + + SetLayoutTextureDirty(); + } + + public void ModifyMesh(VertexHelper verts) + { + if (!isActiveAndEnabled) return; + + // For when pressing play while in prefab mode + if (!SpriteMesh) SpriteMesh = new Mesh(); + verts.FillMesh(SpriteMesh); + + SetLayoutTextureDirty(); + } + + void SetLayoutTextureDirty() + { + SetLayoutDirty(); + SetTextureDirty(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Invalidator.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Invalidator.cs.meta new file mode 100644 index 0000000..e57e775 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Invalidator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8ed7fd00d57e48cdb5a496fb99282d83 +timeCreated: 1594977538 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Plugins.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Plugins.cs new file mode 100644 index 0000000..078ccca --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Plugins.cs @@ -0,0 +1,109 @@ +using System.Collections.Generic; +using System.Linq; +using LeTai.TrueShadow.PluginInterfaces; +using UnityEngine; +using UnityEngine.UI; + +namespace LeTai.TrueShadow +{ +public partial class TrueShadow +{ + ITrueShadowCasterMaterialProvider casterMaterialProvider; + ITrueShadowCasterMaterialPropertiesModifier casterMaterialPropertiesModifier; + ITrueShadowCasterMeshModifier casterMeshModifier; + ITrueShadowCasterClearColorProvider casterClearColorProvider; + ITrueShadowRendererMaterialProvider rendererMaterialProvider; + ITrueShadowRendererMaterialModifier rendererMaterialModifier; + ITrueShadowRendererMeshModifier rendererMeshModifier; + + public bool UsingRendererMaterialProvider => rendererMaterialProvider != null; + + void InitializePlugins() + { + casterMaterialProvider = GetComponent(); + casterMaterialPropertiesModifier = GetComponent(); + casterMeshModifier = GetComponent(); + casterClearColorProvider = GetComponent(); + if (casterClearColorProvider != null) + ColorBleedMode = ColorBleedMode.Plugin; + + rendererMaterialProvider = GetComponent(); + rendererMaterialModifier = GetComponent(); + rendererMeshModifier = GetComponent(); + } + + public virtual Material GetShadowCastingMaterial() + { + return casterMaterialProvider != null + ? casterMaterialProvider.GetTrueShadowCasterMaterial() + : Graphic.material; + } + + public virtual void ModifyShadowCastingMaterialProperties(MaterialPropertyBlock propertyBlock) + { + casterMaterialPropertiesModifier?.ModifyTrueShadowCasterMaterialProperties(propertyBlock); + } + + public virtual void ModifyShadowCastingMesh(Mesh mesh) + { + casterMeshModifier?.ModifyTrueShadowCasterMesh(mesh); + + // Caster can be semi-transparent, but cutout requires mostly opaque stencil. + // Setting alpha to 1 in fragment can't work because of antialiasing. + MakeOpaque(mesh); + } + + readonly List meshColors = new List(4); + List meshColorsOpaque = new List(4); + + void MakeOpaque(Mesh mesh) + { + if (shadowAsSibling) + return; + + mesh.GetColors(meshColors); + var meshColorCount = meshColors.Count; + + if (meshColorCount < 1) return; + + if (meshColorsOpaque.Count == meshColorCount) + { + // Assuming vertex colors are identical + // TODO: This is the case for builtin graphics, but userscript may invalidate that. + if (meshColors[0].a == meshColorsOpaque[0].a) + return; + } + else + { + // TODO: This assumed vertex count change infrequently. Is not the case with Text + meshColorsOpaque.Clear(); + meshColorsOpaque.AddRange(Enumerable.Repeat(new Color32(0, 0, 0, 0), meshColorCount)); + } + + for (var i = 0; i < meshColorCount; i++) + { + var c = meshColors[i]; + c.a = 255; + + meshColorsOpaque[i] = c; + } + + mesh.SetColors(meshColorsOpaque); + } + + public virtual Material GetShadowRenderingNormalMaterial() + { + return rendererMaterialProvider?.GetTrueShadowRendererMaterial(); + } + + public virtual void ModifyShadowRendererMaterial(Material baseMaterial) + { + rendererMaterialModifier?.ModifyTrueShadowRendererMaterial(baseMaterial); + } + + public virtual void ModifyShadowRendererMesh(VertexHelper vertexHelper) + { + rendererMeshModifier?.ModifyTrueShadowRenderMesh(vertexHelper); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Plugins.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Plugins.cs.meta new file mode 100644 index 0000000..12fabe9 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.Plugins.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c652c1f825c14084a6d24c61de8f7772 +timeCreated: 1597650075 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.cs new file mode 100644 index 0000000..6e63dd3 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.cs @@ -0,0 +1,565 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.Serialization; +using UnityEngine.UI; + +namespace LeTai.TrueShadow +{ +[RequireComponent(typeof(Graphic))] +// Doesn't seem to cause problem any more. Hmm +// [DisallowMultipleComponent] +[HelpURL("https://leloctai.com/trueshadow/docs/articles/customize.html")] +[ExecuteAlways] +public partial class TrueShadow : UIBehaviour, IMeshModifier, ICanvasElement +{ + static readonly Color DEFAULT_COLOR = new Color(0, 0, 0, .3f); + + [Tooltip("Size of the shadow")] + [SerializeField] float size = 32; + + [Tooltip("Spread of the shadow")] + [SpreadSlider] + [SerializeField] float spread = 0; + + [Tooltip("Direction to offset the shadow toward")] + [Knob] + [SerializeField] float offsetAngle = 90; + + [Tooltip("How far to offset the shadow")] + [SerializeField] float offsetDistance = 8; + + [SerializeField] Vector2 offset = Vector2.zero; + + [Tooltip("Tint the shadow")] + [SerializeField] Color color = DEFAULT_COLOR; + + [Tooltip("Inset shadow")] + [InsetToggle] + [SerializeField] bool inset = false; + + [Tooltip("Blend mode of the shadow")] + [SerializeField] BlendMode blendMode; + + [FormerlySerializedAs("multiplyCasterAlpha")] + [Tooltip("Allow shadow to cross-fade with caster")] + [SerializeField] bool useCasterAlpha = true; + + [Tooltip("Ignore the shadow caster's color, so you can choose specific color for your shadow")] + [SerializeField] bool ignoreCasterColor = false; + + [Tooltip( + "How to obtain the color of the area outside of the source image. Automatically set based on Blend Mode. You should only change this setting if you are using some very custom UI that require it")] + [SerializeField] ColorBleedMode colorBleedMode; + + [Tooltip("Position the shadow GameObject as previous sibling of the UI element")] + [SerializeField] bool shadowAsSibling; + + [Tooltip("Cut the source image from the shadow to avoid shadow showing behind semi-transparent UI")] + [SerializeField] bool cutout; + +#pragma warning disable 0649 + [Tooltip( + "Bake the shadow to a sprite to reduce CPU and GPU usage at runtime, at the cost of storage, memory and flexibility")] + [SerializeField] bool baked; +#pragma warning restore 0649 + + [SerializeField] bool modifiedFromInspector = false; + + public float Size + { + get => size; + set + { + var newSize = Mathf.Max(0, value); + if (modifiedFromInspector || !Mathf.Approximately(size, newSize)) + { + modifiedFromInspector = false; + + SetLayoutDirty(); + SetTextureDirty(); + } + + size = newSize; + if (Inset && OffsetDistance > Size) + { + OffsetDistance = Size; + } + } + } + + public float Spread + { + get => spread; + set + { + var newSpread = Mathf.Clamp01(value); + if (modifiedFromInspector || !Mathf.Approximately(spread, newSpread)) + { + modifiedFromInspector = false; + + SetLayoutDirty(); + SetTextureDirty(); + } + + spread = newSpread; + } + } + + public float OffsetAngle + { + get => offsetAngle; + set + { + var newValue = (value + 360f) % 360f; + if (modifiedFromInspector || !Mathf.Approximately(offsetAngle, newValue)) + { + modifiedFromInspector = false; + + SetLayoutDirty(); + if (Cutout) + SetTextureDirty(); + } + + offsetAngle = newValue; + offset = Math.AngleDistanceVector(offsetAngle, offset.magnitude, Vector2.right); + } + } + + public float OffsetDistance + { + get => offsetDistance; + set + { + // Limit offset distance for now. + // In order to properly render larger offset, imprint have to be rendered twice. + // TODO: Implement if no one complain about perf + var newValue = value; + if (Inset) + newValue = Mathf.Clamp(newValue, 0, Size); + else + newValue = Mathf.Max(0, newValue); + if (modifiedFromInspector || !Mathf.Approximately(offsetDistance, newValue)) + { + modifiedFromInspector = false; + + SetLayoutDirty(); + if (Cutout) + SetTextureDirty(); + } + + offsetDistance = newValue; + offset = offset.sqrMagnitude < 1e-6f + ? Math.AngleDistanceVector(offsetAngle, offsetDistance, Vector2.right) + : offset.normalized * offsetDistance; + } + } + + public Color Color + { + get => color; + set + { + if (modifiedFromInspector || value != color) + { + modifiedFromInspector = false; + + SetLayoutDirty(); + } + + color = value; + } + } + + /// + /// Allow shadow to cross-fade with caster + /// + public bool UseCasterAlpha + { + get => useCasterAlpha; + set + { + if (modifiedFromInspector || value != useCasterAlpha) + { + modifiedFromInspector = false; + + SetLayoutDirty(); + } + + useCasterAlpha = value; + } + } + + /// + /// Ignore the shadow caster's color, so you can choose specific color for your shadow. + /// When false, is multiplied with caster's color. + /// + public bool IgnoreCasterColor + { + get => ignoreCasterColor; + set + { + if (modifiedFromInspector || value != ignoreCasterColor) + { + modifiedFromInspector = false; + + SetTextureDirty(); + } + + ignoreCasterColor = value; + } + } + + public bool Inset + { + get => inset; + set + { + if (modifiedFromInspector || value != inset) + { + modifiedFromInspector = false; + + SetTextureDirty(); + } + + inset = value; + + if (Inset && OffsetDistance > Size) + { + OffsetDistance = Size; + } + } + } + + public BlendMode BlendMode + { + get => blendMode; + set + { + // Work around for Unity bug causing references loss + if (!Graphic || !CanvasRenderer) + return; + + blendMode = value; + shadowRenderer.UpdateMaterial(); + + switch (blendMode) + { + case BlendMode.Normal: + case BlendMode.Additive: + case BlendMode.Screen: + case BlendMode.Multiply: + ColorBleedMode = ColorBleedMode.Black; + break; + default: + ColorBleedMode = ColorBleedMode.Black; + break; + } + } + } + + /// + /// How to obtain the color of the area outside of the source image. Automatically set based on Blend Mode. You should only change this setting if you are using some very custom UI that require it. + /// + /// + /// + public ColorBleedMode ColorBleedMode + { + get => colorBleedMode; + set + { + if (modifiedFromInspector || colorBleedMode != value) + { + modifiedFromInspector = false; + + colorBleedMode = value; + SetTextureDirty(); + } + } + } + + /// + /// The area where the alpha channel = 0 can be either 0, or the color of the edge of the texture, depend on how the texture was authored. + /// Normally this is not visible, but when blurred, the alpha in these area will become greater than 0 + /// Depend on the blendmode, different color for this clear area may be desired. + /// + /// You can provide custom clear color by implementing , and set this to Plugin + /// + /// + public Color ClearColor + { + get + { + switch (colorBleedMode) + { + case ColorBleedMode.ImageColor: + return Graphic.color.WithA(0); + case ColorBleedMode.ShadowColor: + return Color.WithA(0); + case ColorBleedMode.Black: + return Color.clear; + case ColorBleedMode.White: + return new Color(1, 1, 1, 0); + case ColorBleedMode.Plugin: + return casterClearColorProvider?.GetTrueShadowCasterClearColor() ?? Color.clear; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + /// + /// Can't be implemented due to Unity's bug 1280465. Do not use! + /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public bool ShadowAsSibling + { + get => shadowAsSibling; + set + { + shadowAsSibling = value; + ShadowRenderer.ClearMaskMaterialCache(); + if (shadowAsSibling) + { + ShadowSorter.Instance.Register(this); + } + else + { + ShadowSorter.Instance.UnRegister(this); + if (shadowRenderer) // defensive. undo & prefab make state weird sometime + { + var rendererTransform = shadowRenderer.transform; + rendererTransform.SetParent(transform, true); + rendererTransform.SetSiblingIndex(0); + } + } + } + } + + /// + /// Always true due to . Do not use! + /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public bool Cutout + { + get => !shadowAsSibling || cutout; + set => cutout = value; + } + + [SerializeField] List bakedShadows; + + public Vector2 Offset => offset; + + internal ShadowRenderer shadowRenderer; + + internal Mesh SpriteMesh { get; set; } + internal Graphic Graphic { get; set; } + internal CanvasRenderer CanvasRenderer { get; set; } + internal RectTransform RectTransform { get; private set; } + + internal Texture Content + { + get + { + switch (Graphic) + { + case Image image: + var sprite = image.overrideSprite; + return sprite ? sprite.texture : null; + case RawImage rawImage: return rawImage.texture; + case Text text: return text.mainTexture; + default: return null; + } + } + } + + internal int TextureRevision { get; private set; } + +#if LETAI_TRUESHADOW_DEBUG + public bool alwaysRender; +#endif + + ShadowContainer shadowContainer; + + internal ShadowContainer ShadowContainer + { + get => shadowContainer; + private set => shadowContainer = value; + } + + bool textureDirty; + bool layoutDirty; + internal bool hierachyDirty; + + protected override void Awake() + { + if (ShadowAsSibling) + ShadowSorter.Instance.Register(this); + } + + protected override void OnEnable() + { + base.OnEnable(); + + RectTransform = GetComponent(); + Graphic = GetComponent(); + CanvasRenderer = GetComponent(); + if (!SpriteMesh) SpriteMesh = new Mesh(); + + InitializePlugins(); + + if (bakedShadows == null) + bakedShadows = new List(4); + + InitInvalidator(); + + ShadowRenderer.Initialize(this, ref shadowRenderer); + + Canvas.willRenderCanvases += OnWillRenderCanvas; + + if (Graphic) + Graphic.SetVerticesDirty(); + +#if UNITY_EDITOR + UnityEditor.Undo.undoRedoPerformed += ApplySerializedData; +#endif + +#if UNITY_EDITOR + if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) + UnityEditor.EditorApplication.QueuePlayerLoopUpdate(); +#endif + } + + public void ApplySerializedData() + { + // Changes from prefab apply does not seem to call certain setters. Call manually + Size = size; + Spread = spread; + OffsetAngle = offsetAngle; + OffsetDistance = offsetDistance; + BlendMode = blendMode; + ShadowAsSibling = shadowAsSibling; + + SetHierachyDirty(); + SetLayoutDirty(); + SetTextureDirty(); + + if (shadowRenderer) shadowRenderer.SetMaterialDirty(); + } + + protected override void OnDisable() + { + Canvas.willRenderCanvases -= OnWillRenderCanvas; + TerminateInvalidator(); + + if (shadowRenderer) shadowRenderer.gameObject.SetActive(false); + +#if UNITY_EDITOR + UnityEditor.Undo.undoRedoPerformed -= ApplySerializedData; +#endif + } + + protected override void OnDestroy() + { + ShadowSorter.Instance.UnRegister(this); + if (shadowRenderer) shadowRenderer.Dispose(); + + ShadowFactory.Instance.ReleaseContainer(shadowContainer); + } + + bool ShouldPerformWorks() + { + bool areCanvasRenderersCulled = CanvasRenderer && CanvasRenderer.cull && + shadowRenderer.CanvasRenderer && shadowRenderer.CanvasRenderer.cull; + return isActiveAndEnabled && !areCanvasRenderersCulled; + } + + void LateUpdate() + { + if (!ShouldPerformWorks()) + return; + + CheckTransformDirtied(); + } + + public void Rebuild(CanvasUpdate executing) + { + // Debug.Assert(true, "This should not be called in child mode"); + if (!ShouldPerformWorks()) return; + + if (executing == CanvasUpdate.PostLayout) + { + if (layoutDirty) + { + shadowRenderer.ReLayout(); + layoutDirty = false; + } + } + } + + void OnWillRenderCanvas() + { + if (!isActiveAndEnabled) return; + +#if LETAI_TRUESHADOW_DEBUG + if (alwaysRender) textureDirty = true; +#endif + + if (!ShouldPerformWorks()) return; + + if (textureDirty && Graphic && Graphic.canvas) + { + ShadowFactory.Instance.Get(new ShadowSettingSnapshot(this), ref shadowContainer); + shadowRenderer.SetTexture(shadowContainer?.Texture); + + textureDirty = false; + } + + if (!shadowAsSibling) + { + if (shadowRenderer.transform.parent != transform) + shadowRenderer.transform.SetParent(RectTransform, true); + + if (shadowRenderer.transform.GetSiblingIndex() != 0) + shadowRenderer.transform.SetSiblingIndex(0); + + UnSetHierachyDirty(); + + if (layoutDirty) + { + shadowRenderer.ReLayout(); + layoutDirty = false; + } + } + } + + public void LayoutComplete() { } + + public void GraphicUpdateComplete() { } + + public void SetTextureDirty() + { + textureDirty = true; + unchecked + { + TextureRevision++; + } + } + + public void SetLayoutDirty() + { + layoutDirty = true; + } + + public void SetHierachyDirty() + { + hierachyDirty = true; + } + + internal void UnSetHierachyDirty() + { + hierachyDirty = false; + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.cs.meta new file mode 100644 index 0000000..2057b64 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/TrueShadow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52c162dc854d2f24fa639ba0623de5ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 098c7906709c37c4bba842c34581765c, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities.meta new file mode 100644 index 0000000..ea81901 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b558dc9ac9dc0944fab510f862e9f264 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ExtensionMethods.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ExtensionMethods.cs new file mode 100644 index 0000000..e6cb7ad --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ExtensionMethods.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; + +namespace LeTai +{ +public static class ExtensionMethods +{ + public static Vector4 ToMinMaxVector(this Rect self) + { + return new Vector4( + self.xMin, + self.yMin, + self.xMax, + self.yMax + ); + } + + static Mesh fullscreenTriangle; + + /// + /// A fullscreen triangle mesh. + /// + static Mesh FullscreenTriangle + { + get + { + if (fullscreenTriangle != null) + return fullscreenTriangle; + + fullscreenTriangle = new Mesh {name = "Fullscreen Triangle"}; + fullscreenTriangle.SetVertices( + new List { + new Vector3(-1f, -1f, 0f), + new Vector3(-1f, 3f, 0f), + new Vector3(3f, -1f, 0f) + } + ); + fullscreenTriangle.SetIndices(new[] {0, 1, 2}, MeshTopology.Triangles, 0, false); + fullscreenTriangle.UploadMeshData(false); + + return fullscreenTriangle; + } + } + + public static void BlitFullscreenTriangle(this CommandBuffer cmd, + RenderTargetIdentifier source, + RenderTargetIdentifier destination, + Material material, + int pass = 0) + { + cmd.SetGlobalTexture("_MainTex", source); + +#if UNITY_2018_2_OR_NEWER + cmd.SetRenderTarget(destination, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store); +#else + cmd.SetRenderTarget(destination); +#endif + + cmd.DrawMesh(FullscreenTriangle, Matrix4x4.identity, material, 0, pass); + } + + internal static bool Approximately(this Rect self, Rect other) + { + return QuickApproximate(self.x, other.x) + && QuickApproximate(self.y, other.y) + && QuickApproximate(self.width, other.width) + && QuickApproximate(self.height, other.height); + } + + //A simpler Mathf.Approximately for our purpose + private static bool QuickApproximate(float a, float b) + { + return Mathf.Abs(b - a) < 1.175494E-38f; + } + + public static Vector3 WithZ(this Vector2 self, float z) + { + return new Vector3(self.x, self.y, z); + } + + public static Color WithA(this Color self, float a) + { + return new Color(self.r, self.g, self.b, a); + } + + public static void NextFrames(this MonoBehaviour behaviour, Action action, int nFrames = 1) + { + behaviour.StartCoroutine(NextFrame(action, nFrames)); + } + + static IEnumerator NextFrame(Action action, int nFrames) + { + for (var i = 0; i < nFrames; i++) + yield return null; + + action(); + } + + public static void SetKeyword(this Material material, string keyword, bool enabled) + { + if (enabled) + material.EnableKeyword(keyword); + else + material.DisableKeyword(keyword); + } + + public static Vector2 Frac(this Vector2 vec) + { + return new Vector2( + vec.x - Mathf.Floor(vec.x), + vec.y - Mathf.Floor(vec.y) + ); + } + + public static Vector2 LocalToScreenPoint(this RectTransform rt, + Vector3 localPoint, + Camera referenceCamera = null) + { + return RectTransformUtility.WorldToScreenPoint(referenceCamera, rt.TransformPoint(localPoint)); + } + + public static Vector2 ScreenToCanvasSize(this RectTransform rt, + Vector2 size, + Camera referenceCamera = null) + { + RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Vector2.zero, referenceCamera, out var start); + RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, size, referenceCamera, out var end); + return end - start; + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ExtensionMethods.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ExtensionMethods.cs.meta new file mode 100644 index 0000000..fcdbb6a --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ExtensionMethods.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7aeb90a879f07043a321b93dcd7310f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/HashCode.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/HashCode.cs new file mode 100644 index 0000000..51d20f3 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/HashCode.cs @@ -0,0 +1,85 @@ +namespace LeTai +{ +// Extended from https://referencesource.microsoft.com/#mscorlib/system/tuple.cs,52 +public class HashUtils +{ + internal static int CombineHashCodes(int h1, int h2) + { + return ((h1 << 5) + h1) ^ h2; + } + + internal static int CombineHashCodes(int h1, int h2, int h3) + { + return CombineHashCodes(CombineHashCodes(h1, h2), h3); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4) + { + return CombineHashCodes(CombineHashCodes(h1, h2), CombineHashCodes(h3, h4)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), h5); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7, h8)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8, int h9) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7, h8), h9); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8, int h9, + int h10) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7, h8), CombineHashCodes(h9, h10)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8, int h9, + int h10, int h11) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7, h8), CombineHashCodes(h9, h10, h11)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8, int h9, + int h10, int h11, int h12) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7, h8), CombineHashCodes(h9, h10, h11, h12)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8, int h9, + int h10, int h11, int h12, int h13) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7, h8), + CombineHashCodes(h9, h10, h11, h12, h13)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8, int h9, + int h10, int h11, int h12, int h13, int h14) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7, h8), + CombineHashCodes(h9, h10, h11, h12, h13, h14)); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8, int h9, + int h10, int h11, int h12, int h13, int h14, int h15) + { + return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7, h8), + CombineHashCodes(h9, h10, h11, h12, h13, h14, h15)); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/HashCode.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/HashCode.cs.meta new file mode 100644 index 0000000..478b814 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/HashCode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aaf5875ff23b4286b3ab38783b8da255 +timeCreated: 1595834828 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/IndexedSet.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/IndexedSet.cs new file mode 100644 index 0000000..f23ca6c --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/IndexedSet.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace LeTai.TrueShadow +{ +class IndexedSet : IList +{ + readonly List list = new List(); + readonly Dictionary dict = new Dictionary(); + + public void Add(T item) + { + dict.Add(item, list.Count); + list.Add(item); + } + + public bool AddUnique(T item) + { + if (dict.ContainsKey(item)) + return false; + + dict.Add(item, list.Count); + list.Add(item); + + return true; + } + + public bool Remove(T item) + { + if (!dict.TryGetValue(item, out var index)) + return false; + + RemoveAt(index); + return true; + } + + public void Remove(Predicate match) + { + int i = 0; + while (i < list.Count) + { + T item = list[i]; + if (match(item)) + Remove(item); + else + i++; + } + } + + public void Clear() + { + list.Clear(); + dict.Clear(); + } + + public bool Contains(T item) + { + return dict.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + list.CopyTo(array, arrayIndex); + } + + public int Count => list.Count; + + public bool IsReadOnly => false; + + public int IndexOf(T item) + { + if (dict.TryGetValue(item, out var index)) + return index; + return -1; + } + + public void Insert(int index, T item) + { + //We could support this, but the semantics would be weird. Order is not guaranteed.. + throw new NotSupportedException( + "Random Insertion is semantically invalid, since this structure does not guarantee ordering."); + } + + public void RemoveAt(int index) + { + T item = list[index]; + dict.Remove(item); + if (index == list.Count - 1) + list.RemoveAt(index); + else + { + int replaceItemIndex = list.Count - 1; + T replaceItem = list[replaceItemIndex]; + list[index] = replaceItem; + dict[replaceItem] = index; + list.RemoveAt(replaceItemIndex); + } + } + + public T this[int index] + { + get => list[index]; + set + { + T item = list[index]; + dict.Remove(item); + list[index] = value; + dict.Add(item, index); + } + } + + //Sorts the internal list, this makes the exposed index accessor sorted as well. + //But note that any insertion or deletion, can unorder the collection again. + public void Sort(Comparison sortLayoutFunction) + { + //There might be better ways to sort and keep the dictionary index up to date. + list.Sort(sortLayoutFunction); + //Rebuild the dictionary index. + for (int i = 0; i < list.Count; ++i) + { + T item = list[i]; + dict[item] = i; + } + } + + + public IEnumerator GetEnumerator() + { + return list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/IndexedSet.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/IndexedSet.cs.meta new file mode 100644 index 0000000..3c9a507 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/IndexedSet.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 57517cc9ab1945abb176a2d7f6545890 +timeCreated: 1593673391 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Math.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Math.cs new file mode 100644 index 0000000..720760d --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Math.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using UnityEngine; +using static UnityEngine.Mathf; + +namespace LeTai.TrueShadow +{ +public static class Math +{ + public static float Angle360(Vector2 from, Vector2 to) + { + float angle = Vector2.SignedAngle(from, to); + return angle < 0 ? 360 + angle : angle; + } + + public static Vector2 AngleDistanceVector(float angle, float distance, Vector2 zeroVector) + { + return Quaternion.Euler(0, 0, -angle) * zeroVector * distance; + } + + public static Vector2 Rotate(this Vector2 v, float angle) + { + var rad = angle * Deg2Rad; + var s = Sin(rad); + var c = Cos(rad); + return new Vector2(c * v.x - s * v.y, + s * v.x + c * v.y); + } +} + +public class FloatComparer : IEqualityComparer +{ + readonly float tolerant; + readonly int digits; + + public FloatComparer(int digits = 4) + { + this.digits = digits; + tolerant = 1f / Pow(10, digits); + } + + public bool Equals(float x, float y) + { + return System.Math.Abs(x - y) < tolerant; + } + + public int GetHashCode(float obj) + { + return System.Math.Round(obj, digits).GetHashCode(); + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Math.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Math.cs.meta new file mode 100644 index 0000000..6192962 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Math.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61b5b7160b6742c4bb4d5883d08e64e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/PropertyDrawerAttributes.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/PropertyDrawerAttributes.cs new file mode 100644 index 0000000..75c91bf --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/PropertyDrawerAttributes.cs @@ -0,0 +1,10 @@ +using UnityEngine; + +namespace LeTai.TrueShadow +{ +public class KnobAttribute : PropertyAttribute { } + +public class ToggleButtonsAttribute : PropertyAttribute { } + +public class InsetToggleAttribute : ToggleButtonsAttribute { } +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/PropertyDrawerAttributes.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/PropertyDrawerAttributes.cs.meta new file mode 100644 index 0000000..3596a2c --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/PropertyDrawerAttributes.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 60900a16da6d4f44b89f9272039ea4f1 +timeCreated: 1594637349 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ShaderID.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ShaderID.cs new file mode 100644 index 0000000..56422e6 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ShaderID.cs @@ -0,0 +1,18 @@ +using UnityEngine; + +namespace LeTai +{ +public static class ShaderId +{ + public static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex"); + public static readonly int SHADOW_TEX = Shader.PropertyToID("_ShadowTex"); + public static readonly int CLIP_RECT = Shader.PropertyToID("_ClipRect"); + public static readonly int COLOR_MASK = Shader.PropertyToID("_ColorMask"); + public static readonly int STENCIL_OP = Shader.PropertyToID("_StencilOp"); + public static readonly int STENCIL_ID = Shader.PropertyToID("_Stencil"); + public static readonly int STENCIL_READ_MASK = Shader.PropertyToID("_StencilReadMask"); + public static readonly int OFFSET = Shader.PropertyToID("_Offset"); + public static readonly int OVERFLOW_ALPHA = Shader.PropertyToID("_OverflowAlpha"); + public static readonly int ALPHA_MULTIPLIER = Shader.PropertyToID("_AlphaMultiplier"); +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ShaderID.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ShaderID.cs.meta new file mode 100644 index 0000000..c366271 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/ShaderID.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b73a242c1b944cac9af5b62106473319 +timeCreated: 1592809972 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/SpreadSliderAttribute.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/SpreadSliderAttribute.cs new file mode 100644 index 0000000..d80cba6 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/SpreadSliderAttribute.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +namespace LeTai.TrueShadow +{ +public class SpreadSliderAttribute : PropertyAttribute +{ + public SpreadSliderAttribute() { } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/SpreadSliderAttribute.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/SpreadSliderAttribute.cs.meta new file mode 100644 index 0000000..153a4df --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/SpreadSliderAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 97cb59689d014af2a249b20864599719 +timeCreated: 1615190395 \ No newline at end of file diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Utility.cs b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Utility.cs new file mode 100644 index 0000000..75f197c --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Utility.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using Object = UnityEngine.Object; + + +namespace LeTai +{ +public static class Utility +{ + public static void LogList(IEnumerable list, Func getData) + { + StringBuilder sb = new StringBuilder(); + + int i = 0; + foreach (T el in list) + { + sb.Append(i + ": "); + sb.Append(getData(el).ToString()); + sb.Append("\n"); + i++; + } + + Debug.Log(sb.ToString()); + } + + public static int SimplePingPong(int t, int max) + { + if (t > max) + return 2 * max - t; + return t; + } + + public static void SafeDestroy(Object obj) + { + if (obj != null) + { + if (Application.isPlaying) + { + if (obj is GameObject go) + { + go.transform.parent = null; + } + + Object.Destroy(obj); + } + else Object.DestroyImmediate(obj); + } + } +} +} diff --git a/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Utility.cs.meta b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Utility.cs.meta new file mode 100644 index 0000000..0dd2ac2 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/Scripts/Utilities/Utility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e8dc67630fb04cc4dbd94a8ccc8fe620 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Le Tai's Asset/TrueShadow/changelog.txt b/Assets/Le Tai's Asset/TrueShadow/changelog.txt new file mode 100644 index 0000000..aed0e95 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/changelog.txt @@ -0,0 +1,78 @@ +v0.9.0rc: + This is the last release of the beta cycle. Some breaking changes was introduced, please check the included migration tool for more info. + - Major re-haul of the blending system. This produce better looking shadow, especially for textured shadow. + - Added Screen blend mode. It's similar to Additive, but softer on bright background. + - Many 3rd party assets are now compatible without any additional integration. If you're using any custom integration code, please remove them. + - Shadow now fit tighter in some edge case. + - Shadow resolution now scale properly with Canvas Scaler setting. + +v0.5.1: + - Fix compatibility with "Use 32-bit Display Buffer" option + +v0.5.0: + - Shadow spread + +v0.4.4: + - Minimize gap between shadows and caster. + - Fix canvas being dirtied when not needed. + +v0.4.3: + - Fix script not compiling in certain setup + - Update documentation + +v0.4.2: + - UI Text is now supported! + - Some minor fixes and optimizations + +v0.3.6: + - Clean up: remove some buggy features, fix typos, improve wording + - Make shadow more visible by default + - Minor optimizations. + +v0.3.5: + - Ground work on supporting additional types of Graphics (Text, SVG, 3rd parties) + - Fix editor problem on non-English OS + +v0.3.3: + - Fix world space Z rotation + +v0.3.2: + - Fix inset shadow offset artifact + +v0.3.1: + - World Space Canvas is now supported. Rotated UI also work more correctly + - Allow enabling/disabling caster Graphic alpha affecting shadow + - Fix shadows being incorrectly generated when the caster Graphic is semi transparent + - Fix shadows being jaggy in certain case, including the demo scene + - Fix interaction between 0-size shadow and sprite mip-map + - Hide scene view gizmo + - Various minor fixes and improvements + +v0.2.0: + - New: Inset shadows! + - Neumorphism! + - Helper script to make shadow inset when pressed + - New: Option to ignore shadow caster's color. This mean shadow can now be brighter than the shadow caster, for example, to emulate shiny edge + - New: Multiple shadows support! Just add more True Shadow component to a single UI + + Checkout the Neumorphism demo scene to see the new features in action! + + Also: + - Added Component icon + - Fix shadows being too small + - Many other bug fixes and performance improvements + +v0.1.3: + - Fix shadow size calculation. As a result, shadow should no longer be clipped. But for real this time + - Fix 1 pixel gap between shadow and shadow caster that show up in certain cases + - Use border mip map for demo sprites to avoid artifact when shadow size is 0 + - Fix nullref error when you delete True Shadow component + +v0.1.2: + - Fix shadow size calculation. As a result, shadow should no longer be clipped + - Documentation button on True Shadow component now lead to the correct site instead of Unity's generic scripting page + - Some minor fixes and performance optimization + +v0.1.1: + - Fix Shadow not show up immediately + - Fix Additive Blend mode diff --git a/Assets/Le Tai's Asset/TrueShadow/changelog.txt.meta b/Assets/Le Tai's Asset/TrueShadow/changelog.txt.meta new file mode 100644 index 0000000..b648369 --- /dev/null +++ b/Assets/Le Tai's Asset/TrueShadow/changelog.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 550f012132e9a1e49a6d198d463f7b61 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 5cd5606..1c6747a 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -601,7 +601,7 @@ PlayerSettings: webGLThreadsSupport: 0 webGLDecompressionFallback: 0 scriptingDefineSymbols: - 1: UNITY_POST_PROCESSING_STACK_V2;BOLT_CLOUD;BOLT_1_3_OR_NEWER + 1: UNITY_POST_PROCESSING_STACK_V2;BOLT_CLOUD;BOLT_1_3_OR_NEWER;LETAI_TRUESHADOW 4: UNITY_POST_PROCESSING_STACK_V2 7: UNITY_POST_PROCESSING_STACK_V2 13: UNITY_POST_PROCESSING_STACK_V2