From 61a09fd6502f39e468ded557fe5ad91d26ced39e Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 23 Jun 2022 17:25:02 +0200 Subject: [PATCH 1/9] [unity] Fixed URP Sprite shader not receiving secondary light shadows. Closes #2098. --- .../Include/Spine-Sprite-ForwardPass-URP.hlsl | 26 +++++++++++++++++++ .../package.json | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl index 7c39640cd..e526d3574 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl @@ -43,6 +43,22 @@ struct VertexOutputLWRP /////////////////////////////////////////////////////////////////////////////// // Vertex and Fragment functions // /////////////////////////////////////////////////////////////////////////////// +#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF) +half4 CalculateShadowMaskBackwardsCompatible(InputData inputData) +{ + // To ensure backward compatibility we have to avoid using shadowMask input, as it is not present in older shaders +#if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON) + half4 shadowMask = inputData.shadowMask; +#elif !defined (LIGHTMAP_ON) + half4 shadowMask = unity_ProbesOcclusion; +#else + half4 shadowMask = half4(1, 1, 1, 1); +#endif + + return shadowMask; +} +#endif + half3 LightweightLightVertexSimplified(float3 positionWS, half3 normalWS) { #ifdef _MAIN_LIGHT_VERTEX Light mainLight = GetMainLight(); @@ -101,7 +117,12 @@ half4 LightweightFragmentPBRSimplified(InputData inputData, half4 texAlbedoAlpha int pixelLightCount = GetAdditionalLightsCount(); for (int i = 0; i < pixelLightCount; ++i) { +#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF) + half4 shadowMask = CalculateShadowMaskBackwardsCompatible(inputData); + Light light = GetAdditionalLight(i, inputData.positionWS, shadowMask); +#else Light light = GetAdditionalLight(i, inputData.positionWS); +#endif finalColor += LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS); } #endif @@ -142,7 +163,12 @@ half4 LightweightFragmentBlinnPhongSimplified(InputData inputData, half4 texDiff int pixelLightCount = GetAdditionalLightsCount(); for (int i = 0; i < pixelLightCount; ++i) { +#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF) + half4 shadowMask = CalculateShadowMaskBackwardsCompatible(inputData); + Light light = GetAdditionalLight(i, inputData.positionWS, shadowMask); +#else Light light = GetAdditionalLight(i, inputData.positionWS); +#endif half3 attenuation = (light.distanceAttenuation * light.shadowAttenuation); half3 attenuatedLightColor = light.color * attenuation; #ifndef _DIFFUSE_RAMP diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/package.json b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/package.json index bc6093559..b2776c014 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/package.json +++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.urp-shaders", "displayName": "Spine Universal RP Shaders", "description": "This plugin provides universal render pipeline (URP) shaders for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", - "version": "4.1.2", + "version": "4.1.3", "unity": "2019.3", "author": { "name": "Esoteric Software", From 6e659dc08b1718ced403342caa832f47128925ef Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Mon, 27 Jun 2022 14:03:12 +0200 Subject: [PATCH 2/9] [unity] SkeletonGraphic did not expose UI base class `MaskableGraphic` `Maskable` property. --- .../spine-unity/Editor/Components/SkeletonGraphicInspector.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonGraphicInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonGraphicInspector.cs index c455f0d73..92ad6ec62 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonGraphicInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonGraphicInspector.cs @@ -55,7 +55,7 @@ namespace Spine.Unity.Editor { SerializedProperty initialFlipX, initialFlipY; SerializedProperty meshGeneratorSettings; SerializedProperty allowMultipleCanvasRenderers, separatorSlotNames, enableSeparatorSlots, updateSeparatorPartLocation; - SerializedProperty raycastTarget; + SerializedProperty raycastTarget, maskable; SkeletonGraphic thisSkeletonGraphic; protected bool isInspectingPrefab; @@ -96,6 +96,7 @@ namespace Spine.Unity.Editor { material = so.FindProperty("m_Material"); color = so.FindProperty("m_Color"); raycastTarget = so.FindProperty("m_RaycastTarget"); + maskable = so.FindProperty("m_Maskable"); // SkeletonRenderer additiveMaterial = so.FindProperty("additiveMaterial"); @@ -282,6 +283,7 @@ namespace Spine.Unity.Editor { EditorGUILayout.Space(); EditorGUILayout.LabelField("UI", EditorStyles.boldLabel); EditorGUILayout.PropertyField(raycastTarget); + if (maskable != null) EditorGUILayout.PropertyField(maskable); EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5)); EditorGUILayout.PrefixLabel("Match RectTransform with Mesh"); From 0a32573581c82e8eab77db4f2873f8626467a5e1 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 28 Jun 2022 15:57:04 +0200 Subject: [PATCH 3/9] [unity] Fixed compile error due to not all 2020.3 and 2021.1 versions having SaveAssetIfDirty. Closes #2101. --- .../Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs index 12d836ea5..5f34f0c0f 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs @@ -36,7 +36,7 @@ #define HAS_ON_POSTPROCESS_PREFAB #endif -#if UNITY_2020_3_OR_NEWER +#if (UNITY_2020_3 && UNITY_2020_3_16_OR_NEWER) || UNITY_2021_1_17_OR_NEWER #define HAS_SAVE_ASSET_IF_DIRTY #endif From ab28b77c70e3aa766be5bdb759d7aedac9fd0bde Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 28 Jun 2022 16:32:24 +0200 Subject: [PATCH 4/9] [unity] Fixed previous bugfix, Unity defines UNITY_2020_3_OR_NEWER and UNITY_2020_3_16 but not UNITY_2020_3_16_OR_NEWER. See #2101. --- .../spine-unity/Editor/Utility/SpineBuildProcessor.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs index 5f34f0c0f..92142ec04 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs @@ -36,6 +36,13 @@ #define HAS_ON_POSTPROCESS_PREFAB #endif +#if !(UNITY_2020_3_1 || UNITY_2020_3_2 || UNITY_2020_3_3 || UNITY_2020_3_4 || UNITY_2020_3_5 || UNITY_2020_3_6 || UNITY_2020_3_7 || UNITY_2020_3_8 || UNITY_2020_3_9 || UNITY_2020_3_10 || UNITY_2020_3_11 || UNITY_2020_3_12 || UNITY_2020_3_13 || UNITY_2020_3_14 || UNITY_2020_3_15) +#define UNITY_2020_3_16_OR_NEWER +#endif +#if !(UNITY_2021_1_1 || UNITY_2021_1_2 || UNITY_2021_1_3 || UNITY_2021_1_4 || UNITY_2021_1_5 || UNITY_2021_1_6 || UNITY_2021_1_7 || UNITY_2021_1_8 || UNITY_2021_1_9 || UNITY_2021_1_10 || UNITY_2021_1_11 || UNITY_2021_1_12 || UNITY_2021_1_13 || UNITY_2021_1_14 || UNITY_2021_1_15 || UNITY_2021_1_16) +#define UNITY_2021_1_17_OR_NEWER +#endif + #if (UNITY_2020_3 && UNITY_2020_3_16_OR_NEWER) || UNITY_2021_1_17_OR_NEWER #define HAS_SAVE_ASSET_IF_DIRTY #endif From 4a6e2fee4bb4a198a4c8a65b7b89c2e05eee5dd0 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Thu, 30 Jun 2022 22:13:49 -0400 Subject: [PATCH 5/9] [libgdx] Rollback SV to LWJGL2 to fix 4.0 SV build. --- spine-libgdx/spine-skeletonviewer/.classpath | 3 +- .../spine/SkeletonViewer.java | 32 ++++++------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/spine-libgdx/spine-skeletonviewer/.classpath b/spine-libgdx/spine-skeletonviewer/.classpath index e8bc7f783..9de1be132 100644 --- a/spine-libgdx/spine-skeletonviewer/.classpath +++ b/spine-libgdx/spine-skeletonviewer/.classpath @@ -9,7 +9,6 @@ - - + diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java index fd0c13f5e..e161eb521 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java @@ -35,9 +35,8 @@ import java.lang.reflect.Field; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Preferences; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3WindowAdapter; +import com.badlogic.gdx.backends.lwjgl.LwjglApplication; +import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; @@ -377,24 +376,13 @@ public class SkeletonViewer extends ApplicationAdapter { } if (dpiScale >= 2.0f) uiScale = 2; - final SkeletonViewer skeletonViewer = new SkeletonViewer(); - Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); - config.disableAudio(true); - config.setWindowedMode((int)(800 * uiScale), (int)(600 * uiScale)); - config.setTitle("Skeleton Viewer " + version); - config.setBackBufferConfig(8, 8, 8, 8, 24, 0, 2); - config.setWindowListener(new Lwjgl3WindowAdapter() { - @Override - public void filesDropped (String[] files) { - for (String file : files) { - for (String endSuffix : endSuffixes) { - for (String dataSuffix : dataSuffixes) { - if (file.endsWith(dataSuffix + endSuffix) && skeletonViewer.loadSkeleton(Gdx.files.absolute(file))) return; - } - } - } - } - }); - new Lwjgl3Application(skeletonViewer, config); + LwjglApplicationConfiguration.disableAudio = true; + LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); + config.width = (int)(800 * uiScale); + config.height = (int)(600 * uiScale); + config.title = "Skeleton Viewer"; + config.allowSoftwareMode = true; + config.samples = 2; + new LwjglApplication(new SkeletonViewer(), config); } } From a2158cb64f786fa3b6f4638b18fb10bc13752332 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Thu, 30 Jun 2022 22:24:46 -0400 Subject: [PATCH 6/9] Revert "[libgdx] Rollback SV to LWJGL2 to fix 4.0 SV build." Nothing to see here! This reverts commit 4a6e2fee4bb4a198a4c8a65b7b89c2e05eee5dd0. --- spine-libgdx/spine-skeletonviewer/.classpath | 3 +- .../spine/SkeletonViewer.java | 32 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/spine-libgdx/spine-skeletonviewer/.classpath b/spine-libgdx/spine-skeletonviewer/.classpath index 9de1be132..e8bc7f783 100644 --- a/spine-libgdx/spine-skeletonviewer/.classpath +++ b/spine-libgdx/spine-skeletonviewer/.classpath @@ -9,6 +9,7 @@ - + + diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java index e161eb521..fd0c13f5e 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java @@ -35,8 +35,9 @@ import java.lang.reflect.Field; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Preferences; -import com.badlogic.gdx.backends.lwjgl.LwjglApplication; -import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3WindowAdapter; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; @@ -376,13 +377,24 @@ public class SkeletonViewer extends ApplicationAdapter { } if (dpiScale >= 2.0f) uiScale = 2; - LwjglApplicationConfiguration.disableAudio = true; - LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); - config.width = (int)(800 * uiScale); - config.height = (int)(600 * uiScale); - config.title = "Skeleton Viewer"; - config.allowSoftwareMode = true; - config.samples = 2; - new LwjglApplication(new SkeletonViewer(), config); + final SkeletonViewer skeletonViewer = new SkeletonViewer(); + Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); + config.disableAudio(true); + config.setWindowedMode((int)(800 * uiScale), (int)(600 * uiScale)); + config.setTitle("Skeleton Viewer " + version); + config.setBackBufferConfig(8, 8, 8, 8, 24, 0, 2); + config.setWindowListener(new Lwjgl3WindowAdapter() { + @Override + public void filesDropped (String[] files) { + for (String file : files) { + for (String endSuffix : endSuffixes) { + for (String dataSuffix : dataSuffixes) { + if (file.endsWith(dataSuffix + endSuffix) && skeletonViewer.loadSkeleton(Gdx.files.absolute(file))) return; + } + } + } + } + }); + new Lwjgl3Application(skeletonViewer, config); } } From cffd11a4b31d14d8e09ba23d4e200208f52a94e9 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 1 Jul 2022 14:34:33 +0200 Subject: [PATCH 7/9] [unity] Fixed Unity defines, see #2101. --- .../spine-unity/Editor/Utility/SpineBuildProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs index 92142ec04..4ed661539 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineBuildProcessor.cs @@ -36,14 +36,14 @@ #define HAS_ON_POSTPROCESS_PREFAB #endif -#if !(UNITY_2020_3_1 || UNITY_2020_3_2 || UNITY_2020_3_3 || UNITY_2020_3_4 || UNITY_2020_3_5 || UNITY_2020_3_6 || UNITY_2020_3_7 || UNITY_2020_3_8 || UNITY_2020_3_9 || UNITY_2020_3_10 || UNITY_2020_3_11 || UNITY_2020_3_12 || UNITY_2020_3_13 || UNITY_2020_3_14 || UNITY_2020_3_15) +#if (UNITY_2020_3 && !(UNITY_2020_3_1 || UNITY_2020_3_2 || UNITY_2020_3_3 || UNITY_2020_3_4 || UNITY_2020_3_5 || UNITY_2020_3_6 || UNITY_2020_3_7 || UNITY_2020_3_8 || UNITY_2020_3_9 || UNITY_2020_3_10 || UNITY_2020_3_11 || UNITY_2020_3_12 || UNITY_2020_3_13 || UNITY_2020_3_14 || UNITY_2020_3_15)) #define UNITY_2020_3_16_OR_NEWER #endif -#if !(UNITY_2021_1_1 || UNITY_2021_1_2 || UNITY_2021_1_3 || UNITY_2021_1_4 || UNITY_2021_1_5 || UNITY_2021_1_6 || UNITY_2021_1_7 || UNITY_2021_1_8 || UNITY_2021_1_9 || UNITY_2021_1_10 || UNITY_2021_1_11 || UNITY_2021_1_12 || UNITY_2021_1_13 || UNITY_2021_1_14 || UNITY_2021_1_15 || UNITY_2021_1_16) +#if (UNITY_2021_1 && !(UNITY_2021_1_1 || UNITY_2021_1_2 || UNITY_2021_1_3 || UNITY_2021_1_4 || UNITY_2021_1_5 || UNITY_2021_1_6 || UNITY_2021_1_7 || UNITY_2021_1_8 || UNITY_2021_1_9 || UNITY_2021_1_10 || UNITY_2021_1_11 || UNITY_2021_1_12 || UNITY_2021_1_13 || UNITY_2021_1_14 || UNITY_2021_1_15 || UNITY_2021_1_16)) #define UNITY_2021_1_17_OR_NEWER #endif -#if (UNITY_2020_3 && UNITY_2020_3_16_OR_NEWER) || UNITY_2021_1_17_OR_NEWER +#if UNITY_2020_3_16_OR_NEWER || UNITY_2021_1_17_OR_NEWER #define HAS_SAVE_ASSET_IF_DIRTY #endif From a85765d4d9edcaebb82a45d277abdba5f23aa733 Mon Sep 17 00:00:00 2001 From: GGgRain <69423737+GGgRain@users.noreply.github.com> Date: Mon, 4 Jul 2022 20:11:56 +0900 Subject: [PATCH 8/9] [ue4] Black Spots and Normal Flipping Bug Fixes By CCW Resolve (Old Title : Normal Generation Improvement) (#2096) * [ue4] Normal Generation Improvement All the vertex normals now face correct direction. It will prevent some of the flicker in some occasions. * [ue4] A Huge Rendering Improvement A really huge upgrade for the rendering. Now the renderer can resolving CCW of the triangles to remove the black spot of some parts, and it now has much more simple normal generation. There is no need to restrict the movement of the joint / bone to avoid some ugly black spots being rendered from now, just do it without even thinking about it! And also the flicking bug that caused by the normal flipping bug has been fixed with it, so now you can use lit material more practically with spine. Try to create some cool scenes with a great light setting! --- .../SpineSkeletonRendererComponent.cpp | 774 +++++++++--------- 1 file changed, 403 insertions(+), 371 deletions(-) diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp index 6fd691122..98f2a6e7e 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp @@ -1,371 +1,403 @@ -/****************************************************************************** - * Spine Runtimes License Agreement - * Last updated January 1, 2020. Replaces all prior versions. - * - * Copyright (c) 2013-2020, Esoteric Software LLC - * - * Integration of the Spine Runtimes into software or otherwise creating - * derivative works of the Spine Runtimes is permitted under the terms and - * conditions of Section 2 of the Spine Editor License Agreement: - * http://esotericsoftware.com/spine-editor-license - * - * Otherwise, it is permitted to integrate the Spine Runtimes into software - * or otherwise create derivative works of the Spine Runtimes (collectively, - * "Products"), provided that each user of the Products must obtain their own - * Spine Editor license and redistribution of the Products in any form must - * include this license and copyright notice. - * - * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, - * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#include "Engine.h" -#include "SpinePluginPrivatePCH.h" -#include "spine/spine.h" -#include - -#define LOCTEXT_NAMESPACE "Spine" - -using namespace spine; - -USpineSkeletonRendererComponent::USpineSkeletonRendererComponent(const FObjectInitializer &ObjectInitializer) - : UProceduralMeshComponent(ObjectInitializer) { - PrimaryComponentTick.bCanEverTick = true; - bTickInEditor = true; - bAutoActivate = true; - - static ConstructorHelpers::FObjectFinder NormalMaterialRef(TEXT("/SpinePlugin/SpineUnlitNormalMaterial")); - NormalBlendMaterial = NormalMaterialRef.Object; - - static ConstructorHelpers::FObjectFinder AdditiveMaterialRef(TEXT("/SpinePlugin/SpineUnlitAdditiveMaterial")); - AdditiveBlendMaterial = AdditiveMaterialRef.Object; - - static ConstructorHelpers::FObjectFinder MultiplyMaterialRef(TEXT("/SpinePlugin/SpineUnlitMultiplyMaterial")); - MultiplyBlendMaterial = MultiplyMaterialRef.Object; - - static ConstructorHelpers::FObjectFinder ScreenMaterialRef(TEXT("/SpinePlugin/SpineUnlitScreenMaterial")); - ScreenBlendMaterial = ScreenMaterialRef.Object; - - TextureParameterName = FName(TEXT("SpriteTexture")); - - worldVertices.ensureCapacity(1024 * 2); -} - -void USpineSkeletonRendererComponent::FinishDestroy() { - Super::FinishDestroy(); -} - -void USpineSkeletonRendererComponent::BeginPlay() { - Super::BeginPlay(); -} - -void USpineSkeletonRendererComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - AActor *owner = GetOwner(); - if (owner) { - UClass *skeletonClass = USpineSkeletonComponent::StaticClass(); - USpineSkeletonComponent *skeleton = Cast(owner->GetComponentByClass(skeletonClass)); - - UpdateRenderer(skeleton); - } -} - -void USpineSkeletonRendererComponent::UpdateRenderer(USpineSkeletonComponent *skeleton) { - if (skeleton && !skeleton->IsBeingDestroyed() && skeleton->GetSkeleton() && skeleton->Atlas) { - skeleton->GetSkeleton()->getColor().set(Color.R, Color.G, Color.B, Color.A); - - if (atlasNormalBlendMaterials.Num() != skeleton->Atlas->atlasPages.Num()) { - atlasNormalBlendMaterials.SetNum(0); - pageToNormalBlendMaterial.Empty(); - atlasAdditiveBlendMaterials.SetNum(0); - pageToAdditiveBlendMaterial.Empty(); - atlasMultiplyBlendMaterials.SetNum(0); - pageToMultiplyBlendMaterial.Empty(); - atlasScreenBlendMaterials.SetNum(0); - pageToScreenBlendMaterial.Empty(); - - for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++) { - AtlasPage *currPage = skeleton->Atlas->GetAtlas()->getPages()[i]; - - UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(NormalBlendMaterial, this); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasNormalBlendMaterials.Add(material); - pageToNormalBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(AdditiveBlendMaterial, this); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasAdditiveBlendMaterials.Add(material); - pageToAdditiveBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(MultiplyBlendMaterial, this); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasMultiplyBlendMaterials.Add(material); - pageToMultiplyBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(ScreenBlendMaterial, this); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasScreenBlendMaterials.Add(material); - pageToScreenBlendMaterial.Add(currPage, material); - } - } else { - pageToNormalBlendMaterial.Empty(); - pageToAdditiveBlendMaterial.Empty(); - pageToMultiplyBlendMaterial.Empty(); - pageToScreenBlendMaterial.Empty(); - - for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++) { - AtlasPage *currPage = skeleton->Atlas->GetAtlas()->getPages()[i]; - UTexture2D *texture = skeleton->Atlas->atlasPages[i]; - - UpdateRendererMaterial(currPage, texture, atlasNormalBlendMaterials[i], NormalBlendMaterial, pageToNormalBlendMaterial); - UpdateRendererMaterial(currPage, texture, atlasAdditiveBlendMaterials[i], AdditiveBlendMaterial, pageToAdditiveBlendMaterial); - UpdateRendererMaterial(currPage, texture, atlasMultiplyBlendMaterials[i], MultiplyBlendMaterial, pageToMultiplyBlendMaterial); - UpdateRendererMaterial(currPage, texture, atlasScreenBlendMaterials[i], ScreenBlendMaterial, pageToScreenBlendMaterial); - } - } - UpdateMesh(skeleton->GetSkeleton()); - } else { - ClearAllMeshSections(); - } -} - -void USpineSkeletonRendererComponent::UpdateRendererMaterial(spine::AtlasPage *CurrentPage, UTexture2D *Texture, - UMaterialInstanceDynamic *&CurrentInstance, UMaterialInterface *ParentMaterial, - TMap &PageToBlendMaterial) { - - UTexture *oldTexture = nullptr; - if (!CurrentInstance || !CurrentInstance->GetTextureParameterValue(TextureParameterName, oldTexture) || - oldTexture != Texture || CurrentInstance->Parent != ParentMaterial) { - - UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(ParentMaterial, this); - material->SetTextureParameterValue(TextureParameterName, Texture); - CurrentInstance = material; - } - PageToBlendMaterial.Add(CurrentPage, CurrentInstance); -} - -void USpineSkeletonRendererComponent::Flush(int &Idx, TArray &Vertices, TArray &Indices, TArray &Normals, TArray &Uvs, TArray &Colors, TArray &Colors2, UMaterialInstanceDynamic *Material) { - if (Vertices.Num() == 0) return; - SetMaterial(Idx, Material); - - bool bShouldCreateCollision = false; - if (bCreateCollision) { - UWorld* world = GetWorld(); - if (world && world->IsGameWorld()) { - bShouldCreateCollision = true; - } - } - - GetBodySetup()->bGenerateMirroredCollision = GetComponentScale().X < 0 || GetComponentScale().Y < 0 || GetComponentScale().Z < 0; - CreateMeshSection(Idx, Vertices, Indices, Normals, Uvs, Colors, TArray(), bShouldCreateCollision); - - Vertices.SetNum(0); - Indices.SetNum(0); - Normals.SetNum(0); - Uvs.SetNum(0); - Colors.SetNum(0); - Colors2.SetNum(0); - Idx++; -} - -void USpineSkeletonRendererComponent::UpdateMesh(Skeleton *Skeleton) { - TArray vertices; - TArray indices; - TArray normals; - TArray uvs; - TArray colors; - TArray darkColors; - - int idx = 0; - int meshSection = 0; - UMaterialInstanceDynamic *lastMaterial = nullptr; - - ClearAllMeshSections(); - - // Early out if skeleton is invisible - if (Skeleton->getColor().a == 0) return; - - float depthOffset = 0; - unsigned short quadIndices[] = {0, 1, 2, 0, 2, 3}; - - for (size_t i = 0; i < Skeleton->getSlots().size(); ++i) { - Vector *attachmentVertices = &worldVertices; - unsigned short *attachmentIndices = nullptr; - int numVertices; - int numIndices; - AtlasRegion *attachmentAtlasRegion = nullptr; - spine::Color attachmentColor; - attachmentColor.set(1, 1, 1, 1); - float *attachmentUvs = nullptr; - - Slot *slot = Skeleton->getDrawOrder()[i]; - Attachment *attachment = slot->getAttachment(); - - if (slot->getColor().a == 0 || !slot->getBone().isActive()) { - clipper.clipEnd(*slot); - continue; - } - - if (!attachment) { - clipper.clipEnd(*slot); - continue; - } - if (!attachment->getRTTI().isExactly(RegionAttachment::rtti) && !attachment->getRTTI().isExactly(MeshAttachment::rtti) && !attachment->getRTTI().isExactly(ClippingAttachment::rtti)) { - clipper.clipEnd(*slot); - continue; - } - - if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { - RegionAttachment *regionAttachment = (RegionAttachment *) attachment; - - // Early out if region is invisible - if (regionAttachment->getColor().a == 0) { - clipper.clipEnd(*slot); - continue; - } - - attachmentColor.set(regionAttachment->getColor()); - attachmentAtlasRegion = (AtlasRegion *) regionAttachment->getRendererObject(); - regionAttachment->computeWorldVertices(slot->getBone(), *attachmentVertices, 0, 2); - attachmentIndices = quadIndices; - attachmentUvs = regionAttachment->getUVs().buffer(); - numVertices = 4; - numIndices = 6; - } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { - MeshAttachment *mesh = (MeshAttachment *) attachment; - - // Early out if region is invisible - if (mesh->getColor().a == 0) { - clipper.clipEnd(*slot); - continue; - } - - attachmentColor.set(mesh->getColor()); - attachmentAtlasRegion = (AtlasRegion *) mesh->getRendererObject(); - mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), *attachmentVertices, 0, 2); - attachmentIndices = mesh->getTriangles().buffer(); - attachmentUvs = mesh->getUVs().buffer(); - numVertices = mesh->getWorldVerticesLength() >> 1; - numIndices = mesh->getTriangles().size(); - } else /* clipping */ { - ClippingAttachment *clip = (ClippingAttachment *) attachment; - clipper.clipStart(*slot, clip); - continue; - } - - // if the user switches the atlas data while not having switched - // to the correct skeleton data yet, we won't find any regions. - // ignore regions for which we can't find a material - UMaterialInstanceDynamic *material = nullptr; - switch (slot->getData().getBlendMode()) { - case BlendMode_Normal: - if (!pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToNormalBlendMaterial[attachmentAtlasRegion->page]; - break; - case BlendMode_Additive: - if (!pageToAdditiveBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToAdditiveBlendMaterial[attachmentAtlasRegion->page]; - break; - case BlendMode_Multiply: - if (!pageToMultiplyBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToMultiplyBlendMaterial[attachmentAtlasRegion->page]; - break; - case BlendMode_Screen: - if (!pageToScreenBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToScreenBlendMaterial[attachmentAtlasRegion->page]; - break; - default: - if (!pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToNormalBlendMaterial[attachmentAtlasRegion->page]; - } - - if (clipper.isClipping()) { - clipper.clipTriangles(attachmentVertices->buffer(), attachmentIndices, numIndices, attachmentUvs, 2); - attachmentVertices = &clipper.getClippedVertices(); - numVertices = clipper.getClippedVertices().size() >> 1; - attachmentIndices = clipper.getClippedTriangles().buffer(); - numIndices = clipper.getClippedTriangles().size(); - attachmentUvs = clipper.getClippedUVs().buffer(); - if (clipper.getClippedTriangles().size() == 0) { - clipper.clipEnd(*slot); - continue; - } - } - - if (lastMaterial != material) { - Flush(meshSection, vertices, indices, normals, uvs, colors, darkColors, lastMaterial); - lastMaterial = material; - idx = 0; - } - - SetMaterial(meshSection, material); - - uint8 r = static_cast(Skeleton->getColor().r * slot->getColor().r * attachmentColor.r * 255); - uint8 g = static_cast(Skeleton->getColor().g * slot->getColor().g * attachmentColor.g * 255); - uint8 b = static_cast(Skeleton->getColor().b * slot->getColor().b * attachmentColor.b * 255); - uint8 a = static_cast(Skeleton->getColor().a * slot->getColor().a * attachmentColor.a * 255); - - float dr = slot->hasDarkColor() ? slot->getDarkColor().r : 0.0f; - float dg = slot->hasDarkColor() ? slot->getDarkColor().g : 0.0f; - float db = slot->hasDarkColor() ? slot->getDarkColor().b : 0.0f; - - float *verticesPtr = attachmentVertices->buffer(); - for (int j = 0; j < numVertices << 1; j += 2) { - colors.Add(FColor(r, g, b, a)); - darkColors.Add(FVector(dr, dg, db)); - vertices.Add(FVector(verticesPtr[j], depthOffset, verticesPtr[j + 1])); - uvs.Add(FVector2D(attachmentUvs[j], attachmentUvs[j + 1])); - } - - int firstIndex = indices.Num(); - for (int j = 0; j < numIndices; j++) { - indices.Add(idx + attachmentIndices[j]); - } - - FVector normal = FVector(0, -1, 0); - if (numVertices > 2 && - FVector::CrossProduct( - vertices[indices[firstIndex + 2]] - vertices[indices[firstIndex]], - vertices[indices[firstIndex + 1]] - vertices[indices[firstIndex]]) - .Y > 0.f) { - normal.Y = 1; - } - for (int j = 0; j < numVertices; j++) { - normals.Add(normal); - } - - idx += numVertices; - depthOffset += this->DepthOffset; - - clipper.clipEnd(*slot); - } - - Flush(meshSection, vertices, indices, normals, uvs, colors, darkColors, lastMaterial); - clipper.clipEnd(); -} - -#undef LOCTEXT_NAMESPACE +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated January 1, 2020. Replaces all prior versions. + * + * Copyright (c) 2013-2020, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include "Engine.h" +#include "SpinePluginPrivatePCH.h" +#include "spine/spine.h" +#include + +#define LOCTEXT_NAMESPACE "Spine" + +using namespace spine; + +USpineSkeletonRendererComponent::USpineSkeletonRendererComponent(const FObjectInitializer& ObjectInitializer) + : UProceduralMeshComponent(ObjectInitializer) { + PrimaryComponentTick.bCanEverTick = true; + bTickInEditor = true; + bAutoActivate = true; + + static ConstructorHelpers::FObjectFinder NormalMaterialRef(TEXT("/SpinePlugin/SpineUnlitNormalMaterial")); + NormalBlendMaterial = NormalMaterialRef.Object; + + static ConstructorHelpers::FObjectFinder AdditiveMaterialRef(TEXT("/SpinePlugin/SpineUnlitAdditiveMaterial")); + AdditiveBlendMaterial = AdditiveMaterialRef.Object; + + static ConstructorHelpers::FObjectFinder MultiplyMaterialRef(TEXT("/SpinePlugin/SpineUnlitMultiplyMaterial")); + MultiplyBlendMaterial = MultiplyMaterialRef.Object; + + static ConstructorHelpers::FObjectFinder ScreenMaterialRef(TEXT("/SpinePlugin/SpineUnlitScreenMaterial")); + ScreenBlendMaterial = ScreenMaterialRef.Object; + + TextureParameterName = FName(TEXT("SpriteTexture")); + + worldVertices.ensureCapacity(1024 * 2); +} + +void USpineSkeletonRendererComponent::FinishDestroy() { + Super::FinishDestroy(); +} + +void USpineSkeletonRendererComponent::BeginPlay() { + Super::BeginPlay(); +} + +void USpineSkeletonRendererComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + AActor* owner = GetOwner(); + if (owner) { + UClass* skeletonClass = USpineSkeletonComponent::StaticClass(); + USpineSkeletonComponent* skeleton = Cast(owner->GetComponentByClass(skeletonClass)); + + UpdateRenderer(skeleton); + } +} + +void USpineSkeletonRendererComponent::UpdateRenderer(USpineSkeletonComponent* skeleton) { + if (skeleton && !skeleton->IsBeingDestroyed() && skeleton->GetSkeleton() && skeleton->Atlas) { + skeleton->GetSkeleton()->getColor().set(Color.R, Color.G, Color.B, Color.A); + + if (atlasNormalBlendMaterials.Num() != skeleton->Atlas->atlasPages.Num()) { + atlasNormalBlendMaterials.SetNum(0); + pageToNormalBlendMaterial.Empty(); + atlasAdditiveBlendMaterials.SetNum(0); + pageToAdditiveBlendMaterial.Empty(); + atlasMultiplyBlendMaterials.SetNum(0); + pageToMultiplyBlendMaterial.Empty(); + atlasScreenBlendMaterials.SetNum(0); + pageToScreenBlendMaterial.Empty(); + + for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++) { + AtlasPage* currPage = skeleton->Atlas->GetAtlas()->getPages()[i]; + + UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(NormalBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); + atlasNormalBlendMaterials.Add(material); + pageToNormalBlendMaterial.Add(currPage, material); + + material = UMaterialInstanceDynamic::Create(AdditiveBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); + atlasAdditiveBlendMaterials.Add(material); + pageToAdditiveBlendMaterial.Add(currPage, material); + + material = UMaterialInstanceDynamic::Create(MultiplyBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); + atlasMultiplyBlendMaterials.Add(material); + pageToMultiplyBlendMaterial.Add(currPage, material); + + material = UMaterialInstanceDynamic::Create(ScreenBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); + atlasScreenBlendMaterials.Add(material); + pageToScreenBlendMaterial.Add(currPage, material); + } + } + else { + pageToNormalBlendMaterial.Empty(); + pageToAdditiveBlendMaterial.Empty(); + pageToMultiplyBlendMaterial.Empty(); + pageToScreenBlendMaterial.Empty(); + + for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++) { + AtlasPage* currPage = skeleton->Atlas->GetAtlas()->getPages()[i]; + UTexture2D* texture = skeleton->Atlas->atlasPages[i]; + + UpdateRendererMaterial(currPage, texture, atlasNormalBlendMaterials[i], NormalBlendMaterial, pageToNormalBlendMaterial); + UpdateRendererMaterial(currPage, texture, atlasAdditiveBlendMaterials[i], AdditiveBlendMaterial, pageToAdditiveBlendMaterial); + UpdateRendererMaterial(currPage, texture, atlasMultiplyBlendMaterials[i], MultiplyBlendMaterial, pageToMultiplyBlendMaterial); + UpdateRendererMaterial(currPage, texture, atlasScreenBlendMaterials[i], ScreenBlendMaterial, pageToScreenBlendMaterial); + } + } + UpdateMesh(skeleton->GetSkeleton()); + } + else { + ClearAllMeshSections(); + } +} + +void USpineSkeletonRendererComponent::UpdateRendererMaterial(spine::AtlasPage* CurrentPage, UTexture2D* Texture, + UMaterialInstanceDynamic*& CurrentInstance, UMaterialInterface* ParentMaterial, + TMap& PageToBlendMaterial) { + + UTexture* oldTexture = nullptr; + if (!CurrentInstance || !CurrentInstance->GetTextureParameterValue(TextureParameterName, oldTexture) || + oldTexture != Texture || CurrentInstance->Parent != ParentMaterial) { + + UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(ParentMaterial, this); + material->SetTextureParameterValue(TextureParameterName, Texture); + CurrentInstance = material; + } + PageToBlendMaterial.Add(CurrentPage, CurrentInstance); +} + +void USpineSkeletonRendererComponent::Flush(int& Idx, TArray& Vertices, TArray& Indices, TArray& Normals, TArray& Uvs, TArray& Colors, TArray& Colors2, UMaterialInstanceDynamic* Material) { + if (Vertices.Num() == 0) return; + SetMaterial(Idx, Material); + + bool bShouldCreateCollision = false; + if (bCreateCollision) { + UWorld* world = GetWorld(); + if (world && world->IsGameWorld()) { + bShouldCreateCollision = true; + } + } + + GetBodySetup()->bGenerateMirroredCollision = GetComponentScale().X < 0 || GetComponentScale().Y < 0 || GetComponentScale().Z < 0; + CreateMeshSection(Idx, Vertices, Indices, Normals, Uvs, Colors, TArray(), bShouldCreateCollision); + + Vertices.SetNum(0); + Indices.SetNum(0); + Normals.SetNum(0); + Uvs.SetNum(0); + Colors.SetNum(0); + Colors2.SetNum(0); + Idx++; +} + +void USpineSkeletonRendererComponent::UpdateMesh(Skeleton* Skeleton) { + TArray vertices; + TArray indices; + TArray normals; + TArray uvs; + TArray colors; + TArray darkColors; + + int idx = 0; + int meshSection = 0; + UMaterialInstanceDynamic* lastMaterial = nullptr; + + ClearAllMeshSections(); + + // Early out if skeleton is invisible + if (Skeleton->getColor().a == 0) return; + + float depthOffset = 0; + unsigned short quadIndices[] = { 0, 1, 2, 0, 2, 3 }; + + for (size_t i = 0; i < Skeleton->getSlots().size(); ++i) { + Vector* attachmentVertices = &worldVertices; + unsigned short* attachmentIndices = nullptr; + int numVertices; + int numIndices; + AtlasRegion* attachmentAtlasRegion = nullptr; + spine::Color attachmentColor; + attachmentColor.set(1, 1, 1, 1); + float* attachmentUvs = nullptr; + + Slot* slot = Skeleton->getDrawOrder()[i]; + Attachment* attachment = slot->getAttachment(); + + if (slot->getColor().a == 0 || !slot->getBone().isActive()) { + clipper.clipEnd(*slot); + continue; + } + + if (!attachment) { + clipper.clipEnd(*slot); + continue; + } + if (!attachment->getRTTI().isExactly(RegionAttachment::rtti) && !attachment->getRTTI().isExactly(MeshAttachment::rtti) && !attachment->getRTTI().isExactly(ClippingAttachment::rtti)) { + clipper.clipEnd(*slot); + continue; + } + + if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment* regionAttachment = (RegionAttachment*)attachment; + + // Early out if region is invisible + if (regionAttachment->getColor().a == 0) { + clipper.clipEnd(*slot); + continue; + } + + attachmentColor.set(regionAttachment->getColor()); + attachmentAtlasRegion = (AtlasRegion*)regionAttachment->getRendererObject(); + regionAttachment->computeWorldVertices(slot->getBone(), *attachmentVertices, 0, 2); + attachmentIndices = quadIndices; + attachmentUvs = regionAttachment->getUVs().buffer(); + numVertices = 4; + numIndices = 6; + } + else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { + MeshAttachment* mesh = (MeshAttachment*)attachment; + + // Early out if region is invisible + if (mesh->getColor().a == 0) { + clipper.clipEnd(*slot); + continue; + } + + attachmentColor.set(mesh->getColor()); + attachmentAtlasRegion = (AtlasRegion*)mesh->getRendererObject(); + mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), *attachmentVertices, 0, 2); + attachmentIndices = mesh->getTriangles().buffer(); + attachmentUvs = mesh->getUVs().buffer(); + numVertices = mesh->getWorldVerticesLength() >> 1; + numIndices = mesh->getTriangles().size(); + } + else /* clipping */ { + ClippingAttachment* clip = (ClippingAttachment*)attachment; + clipper.clipStart(*slot, clip); + continue; + } + + // if the user switches the atlas data while not having switched + // to the correct skeleton data yet, we won't find any regions. + // ignore regions for which we can't find a material + UMaterialInstanceDynamic* material = nullptr; + switch (slot->getData().getBlendMode()) { + case BlendMode_Normal: + if (!pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToNormalBlendMaterial[attachmentAtlasRegion->page]; + break; + case BlendMode_Additive: + if (!pageToAdditiveBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToAdditiveBlendMaterial[attachmentAtlasRegion->page]; + break; + case BlendMode_Multiply: + if (!pageToMultiplyBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToMultiplyBlendMaterial[attachmentAtlasRegion->page]; + break; + case BlendMode_Screen: + if (!pageToScreenBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToScreenBlendMaterial[attachmentAtlasRegion->page]; + break; + default: + if (!pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToNormalBlendMaterial[attachmentAtlasRegion->page]; + } + + if (clipper.isClipping()) { + clipper.clipTriangles(attachmentVertices->buffer(), attachmentIndices, numIndices, attachmentUvs, 2); + attachmentVertices = &clipper.getClippedVertices(); + numVertices = clipper.getClippedVertices().size() >> 1; + attachmentIndices = clipper.getClippedTriangles().buffer(); + numIndices = clipper.getClippedTriangles().size(); + attachmentUvs = clipper.getClippedUVs().buffer(); + if (clipper.getClippedTriangles().size() == 0) { + clipper.clipEnd(*slot); + continue; + } + } + + if (lastMaterial != material) { + Flush(meshSection, vertices, indices, normals, uvs, colors, darkColors, lastMaterial); + lastMaterial = material; + idx = 0; + } + + SetMaterial(meshSection, material); + + uint8 r = static_cast(Skeleton->getColor().r * slot->getColor().r * attachmentColor.r * 255); + uint8 g = static_cast(Skeleton->getColor().g * slot->getColor().g * attachmentColor.g * 255); + uint8 b = static_cast(Skeleton->getColor().b * slot->getColor().b * attachmentColor.b * 255); + uint8 a = static_cast(Skeleton->getColor().a * slot->getColor().a * attachmentColor.a * 255); + + float dr = slot->hasDarkColor() ? slot->getDarkColor().r : 0.0f; + float dg = slot->hasDarkColor() ? slot->getDarkColor().g : 0.0f; + float db = slot->hasDarkColor() ? slot->getDarkColor().b : 0.0f; + + float* verticesPtr = attachmentVertices->buffer(); + for (int j = 0; j < numVertices << 1; j += 2) { + colors.Add(FColor(r, g, b, a)); + darkColors.Add(FVector(dr, dg, db)); + vertices.Add(FVector(verticesPtr[j], depthOffset, verticesPtr[j + 1])); + uvs.Add(FVector2D(attachmentUvs[j], attachmentUvs[j + 1])); + } + + int firstIndex = indices.Num(); + for (int j = 0; j < numIndices; j++) { + indices.Add(idx + attachmentIndices[j]); + } + + + + //Calculate total triangle to add on this loof. + + int TriangleInitialCount = firstIndex / 3; + + int TriangleToAddNum = indices.Num() / 3 - TriangleInitialCount; + + int FirstVertexIndex = vertices.Num() - numVertices; + + //loof through all the triangles and resolve to be reversed if the triangle has winding order as CCW. + + for (int j = 0; j < TriangleToAddNum; j++) { + + const int TargetTringleIndex = firstIndex + j * 3; + + if (FVector::CrossProduct( + vertices[indices[TargetTringleIndex + 2]] - vertices[indices[TargetTringleIndex]], + vertices[indices[TargetTringleIndex + 1]] - vertices[indices[TargetTringleIndex]]).Y < 0.f) + { + + const int32 targetVertex = indices[TargetTringleIndex]; + indices[TargetTringleIndex] = indices[TargetTringleIndex + 2]; + indices[TargetTringleIndex + 2] = targetVertex; + + } + } + + + + FVector normal = FVector(0, 1, 0); + + //Add normals for vertices. + + for (int j = 0; j < numVertices; j++) { + + normals.Add(normal); + + } + + idx += numVertices; + depthOffset += this->DepthOffset; + + clipper.clipEnd(*slot); + } + + Flush(meshSection, vertices, indices, normals, uvs, colors, darkColors, lastMaterial); + clipper.clipEnd(); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file From de5f8cd469055d85b46979c02f935f0f935e58ad Mon Sep 17 00:00:00 2001 From: badlogic Date: Mon, 4 Jul 2022 14:00:50 +0200 Subject: [PATCH 9/9] [ue4] Fix formatting. --- .../SpineSkeletonRendererComponent.cpp | 796 +++++++++--------- 1 file changed, 394 insertions(+), 402 deletions(-) diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp index 98f2a6e7e..6da1fd552 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp @@ -1,403 +1,395 @@ -/****************************************************************************** - * Spine Runtimes License Agreement - * Last updated January 1, 2020. Replaces all prior versions. - * - * Copyright (c) 2013-2020, Esoteric Software LLC - * - * Integration of the Spine Runtimes into software or otherwise creating - * derivative works of the Spine Runtimes is permitted under the terms and - * conditions of Section 2 of the Spine Editor License Agreement: - * http://esotericsoftware.com/spine-editor-license - * - * Otherwise, it is permitted to integrate the Spine Runtimes into software - * or otherwise create derivative works of the Spine Runtimes (collectively, - * "Products"), provided that each user of the Products must obtain their own - * Spine Editor license and redistribution of the Products in any form must - * include this license and copyright notice. - * - * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, - * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#include "Engine.h" -#include "SpinePluginPrivatePCH.h" -#include "spine/spine.h" -#include - -#define LOCTEXT_NAMESPACE "Spine" - -using namespace spine; - -USpineSkeletonRendererComponent::USpineSkeletonRendererComponent(const FObjectInitializer& ObjectInitializer) - : UProceduralMeshComponent(ObjectInitializer) { - PrimaryComponentTick.bCanEverTick = true; - bTickInEditor = true; - bAutoActivate = true; - - static ConstructorHelpers::FObjectFinder NormalMaterialRef(TEXT("/SpinePlugin/SpineUnlitNormalMaterial")); - NormalBlendMaterial = NormalMaterialRef.Object; - - static ConstructorHelpers::FObjectFinder AdditiveMaterialRef(TEXT("/SpinePlugin/SpineUnlitAdditiveMaterial")); - AdditiveBlendMaterial = AdditiveMaterialRef.Object; - - static ConstructorHelpers::FObjectFinder MultiplyMaterialRef(TEXT("/SpinePlugin/SpineUnlitMultiplyMaterial")); - MultiplyBlendMaterial = MultiplyMaterialRef.Object; - - static ConstructorHelpers::FObjectFinder ScreenMaterialRef(TEXT("/SpinePlugin/SpineUnlitScreenMaterial")); - ScreenBlendMaterial = ScreenMaterialRef.Object; - - TextureParameterName = FName(TEXT("SpriteTexture")); - - worldVertices.ensureCapacity(1024 * 2); -} - -void USpineSkeletonRendererComponent::FinishDestroy() { - Super::FinishDestroy(); -} - -void USpineSkeletonRendererComponent::BeginPlay() { - Super::BeginPlay(); -} - -void USpineSkeletonRendererComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - AActor* owner = GetOwner(); - if (owner) { - UClass* skeletonClass = USpineSkeletonComponent::StaticClass(); - USpineSkeletonComponent* skeleton = Cast(owner->GetComponentByClass(skeletonClass)); - - UpdateRenderer(skeleton); - } -} - -void USpineSkeletonRendererComponent::UpdateRenderer(USpineSkeletonComponent* skeleton) { - if (skeleton && !skeleton->IsBeingDestroyed() && skeleton->GetSkeleton() && skeleton->Atlas) { - skeleton->GetSkeleton()->getColor().set(Color.R, Color.G, Color.B, Color.A); - - if (atlasNormalBlendMaterials.Num() != skeleton->Atlas->atlasPages.Num()) { - atlasNormalBlendMaterials.SetNum(0); - pageToNormalBlendMaterial.Empty(); - atlasAdditiveBlendMaterials.SetNum(0); - pageToAdditiveBlendMaterial.Empty(); - atlasMultiplyBlendMaterials.SetNum(0); - pageToMultiplyBlendMaterial.Empty(); - atlasScreenBlendMaterials.SetNum(0); - pageToScreenBlendMaterial.Empty(); - - for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++) { - AtlasPage* currPage = skeleton->Atlas->GetAtlas()->getPages()[i]; - - UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(NormalBlendMaterial, this); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasNormalBlendMaterials.Add(material); - pageToNormalBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(AdditiveBlendMaterial, this); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasAdditiveBlendMaterials.Add(material); - pageToAdditiveBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(MultiplyBlendMaterial, this); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasMultiplyBlendMaterials.Add(material); - pageToMultiplyBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(ScreenBlendMaterial, this); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasScreenBlendMaterials.Add(material); - pageToScreenBlendMaterial.Add(currPage, material); - } - } - else { - pageToNormalBlendMaterial.Empty(); - pageToAdditiveBlendMaterial.Empty(); - pageToMultiplyBlendMaterial.Empty(); - pageToScreenBlendMaterial.Empty(); - - for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++) { - AtlasPage* currPage = skeleton->Atlas->GetAtlas()->getPages()[i]; - UTexture2D* texture = skeleton->Atlas->atlasPages[i]; - - UpdateRendererMaterial(currPage, texture, atlasNormalBlendMaterials[i], NormalBlendMaterial, pageToNormalBlendMaterial); - UpdateRendererMaterial(currPage, texture, atlasAdditiveBlendMaterials[i], AdditiveBlendMaterial, pageToAdditiveBlendMaterial); - UpdateRendererMaterial(currPage, texture, atlasMultiplyBlendMaterials[i], MultiplyBlendMaterial, pageToMultiplyBlendMaterial); - UpdateRendererMaterial(currPage, texture, atlasScreenBlendMaterials[i], ScreenBlendMaterial, pageToScreenBlendMaterial); - } - } - UpdateMesh(skeleton->GetSkeleton()); - } - else { - ClearAllMeshSections(); - } -} - -void USpineSkeletonRendererComponent::UpdateRendererMaterial(spine::AtlasPage* CurrentPage, UTexture2D* Texture, - UMaterialInstanceDynamic*& CurrentInstance, UMaterialInterface* ParentMaterial, - TMap& PageToBlendMaterial) { - - UTexture* oldTexture = nullptr; - if (!CurrentInstance || !CurrentInstance->GetTextureParameterValue(TextureParameterName, oldTexture) || - oldTexture != Texture || CurrentInstance->Parent != ParentMaterial) { - - UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(ParentMaterial, this); - material->SetTextureParameterValue(TextureParameterName, Texture); - CurrentInstance = material; - } - PageToBlendMaterial.Add(CurrentPage, CurrentInstance); -} - -void USpineSkeletonRendererComponent::Flush(int& Idx, TArray& Vertices, TArray& Indices, TArray& Normals, TArray& Uvs, TArray& Colors, TArray& Colors2, UMaterialInstanceDynamic* Material) { - if (Vertices.Num() == 0) return; - SetMaterial(Idx, Material); - - bool bShouldCreateCollision = false; - if (bCreateCollision) { - UWorld* world = GetWorld(); - if (world && world->IsGameWorld()) { - bShouldCreateCollision = true; - } - } - - GetBodySetup()->bGenerateMirroredCollision = GetComponentScale().X < 0 || GetComponentScale().Y < 0 || GetComponentScale().Z < 0; - CreateMeshSection(Idx, Vertices, Indices, Normals, Uvs, Colors, TArray(), bShouldCreateCollision); - - Vertices.SetNum(0); - Indices.SetNum(0); - Normals.SetNum(0); - Uvs.SetNum(0); - Colors.SetNum(0); - Colors2.SetNum(0); - Idx++; -} - -void USpineSkeletonRendererComponent::UpdateMesh(Skeleton* Skeleton) { - TArray vertices; - TArray indices; - TArray normals; - TArray uvs; - TArray colors; - TArray darkColors; - - int idx = 0; - int meshSection = 0; - UMaterialInstanceDynamic* lastMaterial = nullptr; - - ClearAllMeshSections(); - - // Early out if skeleton is invisible - if (Skeleton->getColor().a == 0) return; - - float depthOffset = 0; - unsigned short quadIndices[] = { 0, 1, 2, 0, 2, 3 }; - - for (size_t i = 0; i < Skeleton->getSlots().size(); ++i) { - Vector* attachmentVertices = &worldVertices; - unsigned short* attachmentIndices = nullptr; - int numVertices; - int numIndices; - AtlasRegion* attachmentAtlasRegion = nullptr; - spine::Color attachmentColor; - attachmentColor.set(1, 1, 1, 1); - float* attachmentUvs = nullptr; - - Slot* slot = Skeleton->getDrawOrder()[i]; - Attachment* attachment = slot->getAttachment(); - - if (slot->getColor().a == 0 || !slot->getBone().isActive()) { - clipper.clipEnd(*slot); - continue; - } - - if (!attachment) { - clipper.clipEnd(*slot); - continue; - } - if (!attachment->getRTTI().isExactly(RegionAttachment::rtti) && !attachment->getRTTI().isExactly(MeshAttachment::rtti) && !attachment->getRTTI().isExactly(ClippingAttachment::rtti)) { - clipper.clipEnd(*slot); - continue; - } - - if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { - RegionAttachment* regionAttachment = (RegionAttachment*)attachment; - - // Early out if region is invisible - if (regionAttachment->getColor().a == 0) { - clipper.clipEnd(*slot); - continue; - } - - attachmentColor.set(regionAttachment->getColor()); - attachmentAtlasRegion = (AtlasRegion*)regionAttachment->getRendererObject(); - regionAttachment->computeWorldVertices(slot->getBone(), *attachmentVertices, 0, 2); - attachmentIndices = quadIndices; - attachmentUvs = regionAttachment->getUVs().buffer(); - numVertices = 4; - numIndices = 6; - } - else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { - MeshAttachment* mesh = (MeshAttachment*)attachment; - - // Early out if region is invisible - if (mesh->getColor().a == 0) { - clipper.clipEnd(*slot); - continue; - } - - attachmentColor.set(mesh->getColor()); - attachmentAtlasRegion = (AtlasRegion*)mesh->getRendererObject(); - mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), *attachmentVertices, 0, 2); - attachmentIndices = mesh->getTriangles().buffer(); - attachmentUvs = mesh->getUVs().buffer(); - numVertices = mesh->getWorldVerticesLength() >> 1; - numIndices = mesh->getTriangles().size(); - } - else /* clipping */ { - ClippingAttachment* clip = (ClippingAttachment*)attachment; - clipper.clipStart(*slot, clip); - continue; - } - - // if the user switches the atlas data while not having switched - // to the correct skeleton data yet, we won't find any regions. - // ignore regions for which we can't find a material - UMaterialInstanceDynamic* material = nullptr; - switch (slot->getData().getBlendMode()) { - case BlendMode_Normal: - if (!pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToNormalBlendMaterial[attachmentAtlasRegion->page]; - break; - case BlendMode_Additive: - if (!pageToAdditiveBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToAdditiveBlendMaterial[attachmentAtlasRegion->page]; - break; - case BlendMode_Multiply: - if (!pageToMultiplyBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToMultiplyBlendMaterial[attachmentAtlasRegion->page]; - break; - case BlendMode_Screen: - if (!pageToScreenBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToScreenBlendMaterial[attachmentAtlasRegion->page]; - break; - default: - if (!pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) { - clipper.clipEnd(*slot); - continue; - } - material = pageToNormalBlendMaterial[attachmentAtlasRegion->page]; - } - - if (clipper.isClipping()) { - clipper.clipTriangles(attachmentVertices->buffer(), attachmentIndices, numIndices, attachmentUvs, 2); - attachmentVertices = &clipper.getClippedVertices(); - numVertices = clipper.getClippedVertices().size() >> 1; - attachmentIndices = clipper.getClippedTriangles().buffer(); - numIndices = clipper.getClippedTriangles().size(); - attachmentUvs = clipper.getClippedUVs().buffer(); - if (clipper.getClippedTriangles().size() == 0) { - clipper.clipEnd(*slot); - continue; - } - } - - if (lastMaterial != material) { - Flush(meshSection, vertices, indices, normals, uvs, colors, darkColors, lastMaterial); - lastMaterial = material; - idx = 0; - } - - SetMaterial(meshSection, material); - - uint8 r = static_cast(Skeleton->getColor().r * slot->getColor().r * attachmentColor.r * 255); - uint8 g = static_cast(Skeleton->getColor().g * slot->getColor().g * attachmentColor.g * 255); - uint8 b = static_cast(Skeleton->getColor().b * slot->getColor().b * attachmentColor.b * 255); - uint8 a = static_cast(Skeleton->getColor().a * slot->getColor().a * attachmentColor.a * 255); - - float dr = slot->hasDarkColor() ? slot->getDarkColor().r : 0.0f; - float dg = slot->hasDarkColor() ? slot->getDarkColor().g : 0.0f; - float db = slot->hasDarkColor() ? slot->getDarkColor().b : 0.0f; - - float* verticesPtr = attachmentVertices->buffer(); - for (int j = 0; j < numVertices << 1; j += 2) { - colors.Add(FColor(r, g, b, a)); - darkColors.Add(FVector(dr, dg, db)); - vertices.Add(FVector(verticesPtr[j], depthOffset, verticesPtr[j + 1])); - uvs.Add(FVector2D(attachmentUvs[j], attachmentUvs[j + 1])); - } - - int firstIndex = indices.Num(); - for (int j = 0; j < numIndices; j++) { - indices.Add(idx + attachmentIndices[j]); - } - - - - //Calculate total triangle to add on this loof. - - int TriangleInitialCount = firstIndex / 3; - - int TriangleToAddNum = indices.Num() / 3 - TriangleInitialCount; - - int FirstVertexIndex = vertices.Num() - numVertices; - - //loof through all the triangles and resolve to be reversed if the triangle has winding order as CCW. - - for (int j = 0; j < TriangleToAddNum; j++) { - - const int TargetTringleIndex = firstIndex + j * 3; - - if (FVector::CrossProduct( - vertices[indices[TargetTringleIndex + 2]] - vertices[indices[TargetTringleIndex]], - vertices[indices[TargetTringleIndex + 1]] - vertices[indices[TargetTringleIndex]]).Y < 0.f) - { - - const int32 targetVertex = indices[TargetTringleIndex]; - indices[TargetTringleIndex] = indices[TargetTringleIndex + 2]; - indices[TargetTringleIndex + 2] = targetVertex; - - } - } - - - - FVector normal = FVector(0, 1, 0); - - //Add normals for vertices. - - for (int j = 0; j < numVertices; j++) { - - normals.Add(normal); - - } - - idx += numVertices; - depthOffset += this->DepthOffset; - - clipper.clipEnd(*slot); - } - - Flush(meshSection, vertices, indices, normals, uvs, colors, darkColors, lastMaterial); - clipper.clipEnd(); -} - +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated January 1, 2020. Replaces all prior versions. + * + * Copyright (c) 2013-2020, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include "Engine.h" +#include "SpinePluginPrivatePCH.h" +#include "spine/spine.h" +#include + +#define LOCTEXT_NAMESPACE "Spine" + +using namespace spine; + +USpineSkeletonRendererComponent::USpineSkeletonRendererComponent(const FObjectInitializer &ObjectInitializer) + : UProceduralMeshComponent(ObjectInitializer) { + PrimaryComponentTick.bCanEverTick = true; + bTickInEditor = true; + bAutoActivate = true; + + static ConstructorHelpers::FObjectFinder NormalMaterialRef(TEXT("/SpinePlugin/SpineUnlitNormalMaterial")); + NormalBlendMaterial = NormalMaterialRef.Object; + + static ConstructorHelpers::FObjectFinder AdditiveMaterialRef(TEXT("/SpinePlugin/SpineUnlitAdditiveMaterial")); + AdditiveBlendMaterial = AdditiveMaterialRef.Object; + + static ConstructorHelpers::FObjectFinder MultiplyMaterialRef(TEXT("/SpinePlugin/SpineUnlitMultiplyMaterial")); + MultiplyBlendMaterial = MultiplyMaterialRef.Object; + + static ConstructorHelpers::FObjectFinder ScreenMaterialRef(TEXT("/SpinePlugin/SpineUnlitScreenMaterial")); + ScreenBlendMaterial = ScreenMaterialRef.Object; + + TextureParameterName = FName(TEXT("SpriteTexture")); + + worldVertices.ensureCapacity(1024 * 2); +} + +void USpineSkeletonRendererComponent::FinishDestroy() { + Super::FinishDestroy(); +} + +void USpineSkeletonRendererComponent::BeginPlay() { + Super::BeginPlay(); +} + +void USpineSkeletonRendererComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + AActor *owner = GetOwner(); + if (owner) { + UClass *skeletonClass = USpineSkeletonComponent::StaticClass(); + USpineSkeletonComponent *skeleton = Cast(owner->GetComponentByClass(skeletonClass)); + + UpdateRenderer(skeleton); + } +} + +void USpineSkeletonRendererComponent::UpdateRenderer(USpineSkeletonComponent *skeleton) { + if (skeleton && !skeleton->IsBeingDestroyed() && skeleton->GetSkeleton() && skeleton->Atlas) { + skeleton->GetSkeleton()->getColor().set(Color.R, Color.G, Color.B, Color.A); + + if (atlasNormalBlendMaterials.Num() != skeleton->Atlas->atlasPages.Num()) { + atlasNormalBlendMaterials.SetNum(0); + pageToNormalBlendMaterial.Empty(); + atlasAdditiveBlendMaterials.SetNum(0); + pageToAdditiveBlendMaterial.Empty(); + atlasMultiplyBlendMaterials.SetNum(0); + pageToMultiplyBlendMaterial.Empty(); + atlasScreenBlendMaterials.SetNum(0); + pageToScreenBlendMaterial.Empty(); + + for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++) { + AtlasPage *currPage = skeleton->Atlas->GetAtlas()->getPages()[i]; + + UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(NormalBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); + atlasNormalBlendMaterials.Add(material); + pageToNormalBlendMaterial.Add(currPage, material); + + material = UMaterialInstanceDynamic::Create(AdditiveBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); + atlasAdditiveBlendMaterials.Add(material); + pageToAdditiveBlendMaterial.Add(currPage, material); + + material = UMaterialInstanceDynamic::Create(MultiplyBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); + atlasMultiplyBlendMaterials.Add(material); + pageToMultiplyBlendMaterial.Add(currPage, material); + + material = UMaterialInstanceDynamic::Create(ScreenBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); + atlasScreenBlendMaterials.Add(material); + pageToScreenBlendMaterial.Add(currPage, material); + } + } else { + pageToNormalBlendMaterial.Empty(); + pageToAdditiveBlendMaterial.Empty(); + pageToMultiplyBlendMaterial.Empty(); + pageToScreenBlendMaterial.Empty(); + + for (int i = 0; i < skeleton->Atlas->atlasPages.Num(); i++) { + AtlasPage *currPage = skeleton->Atlas->GetAtlas()->getPages()[i]; + UTexture2D *texture = skeleton->Atlas->atlasPages[i]; + + UpdateRendererMaterial(currPage, texture, atlasNormalBlendMaterials[i], NormalBlendMaterial, pageToNormalBlendMaterial); + UpdateRendererMaterial(currPage, texture, atlasAdditiveBlendMaterials[i], AdditiveBlendMaterial, pageToAdditiveBlendMaterial); + UpdateRendererMaterial(currPage, texture, atlasMultiplyBlendMaterials[i], MultiplyBlendMaterial, pageToMultiplyBlendMaterial); + UpdateRendererMaterial(currPage, texture, atlasScreenBlendMaterials[i], ScreenBlendMaterial, pageToScreenBlendMaterial); + } + } + UpdateMesh(skeleton->GetSkeleton()); + } else { + ClearAllMeshSections(); + } +} + +void USpineSkeletonRendererComponent::UpdateRendererMaterial(spine::AtlasPage *CurrentPage, UTexture2D *Texture, + UMaterialInstanceDynamic *&CurrentInstance, UMaterialInterface *ParentMaterial, + TMap &PageToBlendMaterial) { + + UTexture *oldTexture = nullptr; + if (!CurrentInstance || !CurrentInstance->GetTextureParameterValue(TextureParameterName, oldTexture) || + oldTexture != Texture || CurrentInstance->Parent != ParentMaterial) { + + UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(ParentMaterial, this); + material->SetTextureParameterValue(TextureParameterName, Texture); + CurrentInstance = material; + } + PageToBlendMaterial.Add(CurrentPage, CurrentInstance); +} + +void USpineSkeletonRendererComponent::Flush(int &Idx, TArray &Vertices, TArray &Indices, TArray &Normals, TArray &Uvs, TArray &Colors, TArray &Colors2, UMaterialInstanceDynamic *Material) { + if (Vertices.Num() == 0) return; + SetMaterial(Idx, Material); + + bool bShouldCreateCollision = false; + if (bCreateCollision) { + UWorld *world = GetWorld(); + if (world && world->IsGameWorld()) { + bShouldCreateCollision = true; + } + } + + GetBodySetup()->bGenerateMirroredCollision = GetComponentScale().X < 0 || GetComponentScale().Y < 0 || GetComponentScale().Z < 0; + CreateMeshSection(Idx, Vertices, Indices, Normals, Uvs, Colors, TArray(), bShouldCreateCollision); + + Vertices.SetNum(0); + Indices.SetNum(0); + Normals.SetNum(0); + Uvs.SetNum(0); + Colors.SetNum(0); + Colors2.SetNum(0); + Idx++; +} + +void USpineSkeletonRendererComponent::UpdateMesh(Skeleton *Skeleton) { + TArray vertices; + TArray indices; + TArray normals; + TArray uvs; + TArray colors; + TArray darkColors; + + int idx = 0; + int meshSection = 0; + UMaterialInstanceDynamic *lastMaterial = nullptr; + + ClearAllMeshSections(); + + // Early out if skeleton is invisible + if (Skeleton->getColor().a == 0) return; + + float depthOffset = 0; + unsigned short quadIndices[] = {0, 1, 2, 0, 2, 3}; + + for (size_t i = 0; i < Skeleton->getSlots().size(); ++i) { + Vector *attachmentVertices = &worldVertices; + unsigned short *attachmentIndices = nullptr; + int numVertices; + int numIndices; + AtlasRegion *attachmentAtlasRegion = nullptr; + spine::Color attachmentColor; + attachmentColor.set(1, 1, 1, 1); + float *attachmentUvs = nullptr; + + Slot *slot = Skeleton->getDrawOrder()[i]; + Attachment *attachment = slot->getAttachment(); + + if (slot->getColor().a == 0 || !slot->getBone().isActive()) { + clipper.clipEnd(*slot); + continue; + } + + if (!attachment) { + clipper.clipEnd(*slot); + continue; + } + if (!attachment->getRTTI().isExactly(RegionAttachment::rtti) && !attachment->getRTTI().isExactly(MeshAttachment::rtti) && !attachment->getRTTI().isExactly(ClippingAttachment::rtti)) { + clipper.clipEnd(*slot); + continue; + } + + if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment *regionAttachment = (RegionAttachment *) attachment; + + // Early out if region is invisible + if (regionAttachment->getColor().a == 0) { + clipper.clipEnd(*slot); + continue; + } + + attachmentColor.set(regionAttachment->getColor()); + attachmentAtlasRegion = (AtlasRegion *) regionAttachment->getRendererObject(); + regionAttachment->computeWorldVertices(slot->getBone(), *attachmentVertices, 0, 2); + attachmentIndices = quadIndices; + attachmentUvs = regionAttachment->getUVs().buffer(); + numVertices = 4; + numIndices = 6; + } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { + MeshAttachment *mesh = (MeshAttachment *) attachment; + + // Early out if region is invisible + if (mesh->getColor().a == 0) { + clipper.clipEnd(*slot); + continue; + } + + attachmentColor.set(mesh->getColor()); + attachmentAtlasRegion = (AtlasRegion *) mesh->getRendererObject(); + mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), *attachmentVertices, 0, 2); + attachmentIndices = mesh->getTriangles().buffer(); + attachmentUvs = mesh->getUVs().buffer(); + numVertices = mesh->getWorldVerticesLength() >> 1; + numIndices = mesh->getTriangles().size(); + } else /* clipping */ { + ClippingAttachment *clip = (ClippingAttachment *) attachment; + clipper.clipStart(*slot, clip); + continue; + } + + // if the user switches the atlas data while not having switched + // to the correct skeleton data yet, we won't find any regions. + // ignore regions for which we can't find a material + UMaterialInstanceDynamic *material = nullptr; + switch (slot->getData().getBlendMode()) { + case BlendMode_Normal: + if (!pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToNormalBlendMaterial[attachmentAtlasRegion->page]; + break; + case BlendMode_Additive: + if (!pageToAdditiveBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToAdditiveBlendMaterial[attachmentAtlasRegion->page]; + break; + case BlendMode_Multiply: + if (!pageToMultiplyBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToMultiplyBlendMaterial[attachmentAtlasRegion->page]; + break; + case BlendMode_Screen: + if (!pageToScreenBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToScreenBlendMaterial[attachmentAtlasRegion->page]; + break; + default: + if (!pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) { + clipper.clipEnd(*slot); + continue; + } + material = pageToNormalBlendMaterial[attachmentAtlasRegion->page]; + } + + if (clipper.isClipping()) { + clipper.clipTriangles(attachmentVertices->buffer(), attachmentIndices, numIndices, attachmentUvs, 2); + attachmentVertices = &clipper.getClippedVertices(); + numVertices = clipper.getClippedVertices().size() >> 1; + attachmentIndices = clipper.getClippedTriangles().buffer(); + numIndices = clipper.getClippedTriangles().size(); + attachmentUvs = clipper.getClippedUVs().buffer(); + if (clipper.getClippedTriangles().size() == 0) { + clipper.clipEnd(*slot); + continue; + } + } + + if (lastMaterial != material) { + Flush(meshSection, vertices, indices, normals, uvs, colors, darkColors, lastMaterial); + lastMaterial = material; + idx = 0; + } + + SetMaterial(meshSection, material); + + uint8 r = static_cast(Skeleton->getColor().r * slot->getColor().r * attachmentColor.r * 255); + uint8 g = static_cast(Skeleton->getColor().g * slot->getColor().g * attachmentColor.g * 255); + uint8 b = static_cast(Skeleton->getColor().b * slot->getColor().b * attachmentColor.b * 255); + uint8 a = static_cast(Skeleton->getColor().a * slot->getColor().a * attachmentColor.a * 255); + + float dr = slot->hasDarkColor() ? slot->getDarkColor().r : 0.0f; + float dg = slot->hasDarkColor() ? slot->getDarkColor().g : 0.0f; + float db = slot->hasDarkColor() ? slot->getDarkColor().b : 0.0f; + + float *verticesPtr = attachmentVertices->buffer(); + for (int j = 0; j < numVertices << 1; j += 2) { + colors.Add(FColor(r, g, b, a)); + darkColors.Add(FVector(dr, dg, db)); + vertices.Add(FVector(verticesPtr[j], depthOffset, verticesPtr[j + 1])); + uvs.Add(FVector2D(attachmentUvs[j], attachmentUvs[j + 1])); + } + + int firstIndex = indices.Num(); + for (int j = 0; j < numIndices; j++) { + indices.Add(idx + attachmentIndices[j]); + } + + + //Calculate total triangle to add on this loof. + + int TriangleInitialCount = firstIndex / 3; + + int TriangleToAddNum = indices.Num() / 3 - TriangleInitialCount; + + int FirstVertexIndex = vertices.Num() - numVertices; + + //loof through all the triangles and resolve to be reversed if the triangle has winding order as CCW. + + for (int j = 0; j < TriangleToAddNum; j++) { + + const int TargetTringleIndex = firstIndex + j * 3; + + if (FVector::CrossProduct( + vertices[indices[TargetTringleIndex + 2]] - vertices[indices[TargetTringleIndex]], + vertices[indices[TargetTringleIndex + 1]] - vertices[indices[TargetTringleIndex]]) + .Y < 0.f) { + + const int32 targetVertex = indices[TargetTringleIndex]; + indices[TargetTringleIndex] = indices[TargetTringleIndex + 2]; + indices[TargetTringleIndex + 2] = targetVertex; + } + } + + + FVector normal = FVector(0, 1, 0); + + //Add normals for vertices. + + for (int j = 0; j < numVertices; j++) { + + normals.Add(normal); + } + + idx += numVertices; + depthOffset += this->DepthOffset; + + clipper.clipEnd(*slot); + } + + Flush(meshSection, vertices, indices, normals, uvs, colors, darkColors, lastMaterial); + clipper.clipEnd(); +} + #undef LOCTEXT_NAMESPACE \ No newline at end of file