From b21ecba44523d45b5eeac8804ec5861bec75eeaf Mon Sep 17 00:00:00 2001 From: Carlos Ibanez Date: Mon, 14 Jan 2019 12:06:59 -0300 Subject: [PATCH] - (UE4) Changes meant to support PaperZD or any other external plugins which need to interface directly with the renderer. - (UE4) Added skeleton animation caching on SkeletonRenderer to avoid per tick searches which are costly. The skeleton animation component can be setup externally. - (UE4) Added bAutoPlays members to AnimationComponent, so the skeleton doesn't update automatically if its needed. - (UE4) Added SetPlaybackTime method to AnimationComponent. --- .../SpineSkeletonAnimationComponent.cpp | 32 ++- .../SpineSkeletonRendererComponent.cpp | 201 ++++++++++-------- .../Public/SpineSkeletonAnimationComponent.h | 15 ++ .../Public/SpineSkeletonRendererComponent.h | 20 +- 4 files changed, 175 insertions(+), 93 deletions(-) diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp index 16a18edc7..8b5837031 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp @@ -94,7 +94,7 @@ void USpineSkeletonAnimationComponent::TickComponent(float DeltaTime, ELevelTick void USpineSkeletonAnimationComponent::InternalTick(float DeltaTime, bool CallDelegates) { CheckState(); - if (state) { + if (state && bAutoPlaying) { state->update(DeltaTime); state->apply(*skeleton); if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this); @@ -144,6 +144,36 @@ void USpineSkeletonAnimationComponent::FinishDestroy () { Super::FinishDestroy(); } +void USpineSkeletonAnimationComponent::SetAutoPlay(bool bInAutoPlays) +{ + bAutoPlaying = bInAutoPlays; +} + +void USpineSkeletonAnimationComponent::SetPlaybackTime(float InPlaybackTime, bool bCallDelegates) +{ + CheckState(); + + if (state && state->getCurrent(0)) { + spine::Animation* CurrentAnimation = state->getCurrent(0)->getAnimation(); + const float CurrentTime = state->getCurrent(0)->getTrackTime(); + InPlaybackTime = FMath::Clamp(InPlaybackTime, 0.0f, CurrentAnimation->getDuration()); + const float DeltaTime = InPlaybackTime - CurrentTime; + state->update(DeltaTime); + state->apply(*skeleton); + + //Call delegates and perform the world transform + if (bCallDelegates) + { + BeforeUpdateWorldTransform.Broadcast(this); + } + skeleton->updateWorldTransform(); + if (bCallDelegates) + { + AfterUpdateWorldTransform.Broadcast(this); + } + } +} + void USpineSkeletonAnimationComponent::SetTimeScale(float timeScale) { CheckState(); if (state) state->setTimeScale(timeScale); diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp index 6e69f507c..076dc7a01 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp @@ -68,100 +68,123 @@ void USpineSkeletonRendererComponent::BeginPlay () { Super::BeginPlay(); } +USpineSkeletonComponent* USpineSkeletonRendererComponent::GetSkeleton() const +{ + return CachedSkeleton.IsValid() ? CachedSkeleton.Get() : nullptr; +} + +void USpineSkeletonRendererComponent::SetSkeleton(USpineSkeletonComponent* InSkeleton) +{ + CachedSkeleton = InSkeleton; +} + 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)); - - 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(false)->getPages()[i]; - - UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(NormalBlendMaterial, owner); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasNormalBlendMaterials.Add(material); - pageToNormalBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(AdditiveBlendMaterial, owner); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasAdditiveBlendMaterials.Add(material); - pageToAdditiveBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(MultiplyBlendMaterial, owner); - material->SetTextureParameterValue(TextureParameterName, skeleton->Atlas->atlasPages[i]); - atlasMultiplyBlendMaterials.Add(material); - pageToMultiplyBlendMaterial.Add(currPage, material); - - material = UMaterialInstanceDynamic::Create(ScreenBlendMaterial, owner); - 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(false)->getPages()[i]; - - UTexture2D* texture = skeleton->Atlas->atlasPages[i]; - UTexture* oldTexture = nullptr; - - UMaterialInstanceDynamic* current = atlasNormalBlendMaterials[i]; - if(!current || !current->GetTextureParameterValue(TextureParameterName, oldTexture) || oldTexture != texture) { - UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(NormalBlendMaterial, owner); - material->SetTextureParameterValue(TextureParameterName, texture); - atlasNormalBlendMaterials[i] = material; - } - pageToNormalBlendMaterial.Add(currPage, atlasNormalBlendMaterials[i]); - - current = atlasAdditiveBlendMaterials[i]; - if(!current || !current->GetTextureParameterValue(TextureParameterName, oldTexture) || oldTexture != texture) { - UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(AdditiveBlendMaterial, owner); - material->SetTextureParameterValue(TextureParameterName, texture); - atlasAdditiveBlendMaterials[i] = material; - } - pageToAdditiveBlendMaterial.Add(currPage, atlasAdditiveBlendMaterials[i]); - - current = atlasMultiplyBlendMaterials[i]; - if(!current || !current->GetTextureParameterValue(TextureParameterName, oldTexture) || oldTexture != texture) { - UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(MultiplyBlendMaterial, owner); - material->SetTextureParameterValue(TextureParameterName, texture); - atlasMultiplyBlendMaterials[i] = material; - } - pageToMultiplyBlendMaterial.Add(currPage, atlasMultiplyBlendMaterials[i]); - - current = atlasScreenBlendMaterials[i]; - if(!current || !current->GetTextureParameterValue(TextureParameterName, oldTexture) || oldTexture != texture) { - UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(ScreenBlendMaterial, owner); - material->SetTextureParameterValue(TextureParameterName, texture); - atlasScreenBlendMaterials[i] = material; - } - pageToScreenBlendMaterial.Add(currPage, atlasScreenBlendMaterials[i]); - } - } - UpdateMesh(skeleton->GetSkeleton()); - } else { - ClearAllMeshSections(); + //Check if we need to initialize the skeleton cache, this will help us to avoid excessive Find functions when ticking, thus improving performance + if (!GetSkeleton()) + { + USpineSkeletonComponent* FoundSkeletonComponent = GetOwner()->FindComponentByClass(); + if (FoundSkeletonComponent) + { + SetSkeleton(FoundSkeletonComponent); } } + + //Make sure we update the renderer + UpdateRenderer(); +} + +void USpineSkeletonRendererComponent::UpdateRenderer() +{ + USpineSkeletonComponent* skeleton = GetSkeleton(); + 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(false)->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(false)->getPages()[i]; + + UTexture2D* texture = skeleton->Atlas->atlasPages[i]; + UTexture* oldTexture = nullptr; + + UMaterialInstanceDynamic* current = atlasNormalBlendMaterials[i]; + if (!current || !current->GetTextureParameterValue(TextureParameterName, oldTexture) || oldTexture != texture) { + UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(NormalBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, texture); + atlasNormalBlendMaterials[i] = material; + } + pageToNormalBlendMaterial.Add(currPage, atlasNormalBlendMaterials[i]); + + current = atlasAdditiveBlendMaterials[i]; + if (!current || !current->GetTextureParameterValue(TextureParameterName, oldTexture) || oldTexture != texture) { + UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(AdditiveBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, texture); + atlasAdditiveBlendMaterials[i] = material; + } + pageToAdditiveBlendMaterial.Add(currPage, atlasAdditiveBlendMaterials[i]); + + current = atlasMultiplyBlendMaterials[i]; + if (!current || !current->GetTextureParameterValue(TextureParameterName, oldTexture) || oldTexture != texture) { + UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(MultiplyBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, texture); + atlasMultiplyBlendMaterials[i] = material; + } + pageToMultiplyBlendMaterial.Add(currPage, atlasMultiplyBlendMaterials[i]); + + current = atlasScreenBlendMaterials[i]; + if (!current || !current->GetTextureParameterValue(TextureParameterName, oldTexture) || oldTexture != texture) { + UMaterialInstanceDynamic* material = UMaterialInstanceDynamic::Create(ScreenBlendMaterial, this); + material->SetTextureParameterValue(TextureParameterName, texture); + atlasScreenBlendMaterials[i] = material; + } + pageToScreenBlendMaterial.Add(currPage, atlasScreenBlendMaterials[i]); + } + } + UpdateMesh(skeleton->GetSkeleton()); + } + else { + ClearAllMeshSections(); + } } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h index f4a7da2c8..c4dc49f42 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h @@ -194,6 +194,16 @@ public: virtual void TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void FinishDestroy () override; + + //Added functions for manual configuration + + /* Manages if this skeleton should update automatically or is paused. */ + UFUNCTION(BlueprintCallable, Category="Components|Spine|Animation") + void SetAutoPlay(bool bInAutoPlays); + + /* Directly set the time of the current animation, will clamp to animation range. */ + UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation") + void SetPlaybackTime(float InPlaybackTime, bool bCallDelegates = true); // Blueprint functions UFUNCTION(BlueprintCallable, Category="Components|Spine|Animation") @@ -255,4 +265,9 @@ protected: // in transit within a blueprint UPROPERTY() TSet trackEntries; + +private: + /* If the animation should update automatically. */ + UPROPERTY() + bool bAutoPlaying; }; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h index f32f49c2a..b2ae31280 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h @@ -86,10 +86,20 @@ public: UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite) FLinearColor Color = FLinearColor(1, 1, 1, 1); - /** Whether to generate collision geometry for the skeleton, or not. */ - UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite) - bool bCreateCollision; + /** Whether to generate collision geometry for the skeleton, or not. */ + UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite) + bool bCreateCollision; + /* Updates this renderer with the lastest skeleton info. */ + void UpdateRenderer(); + + /* Obtains the cached SkeletonComponent */ + USpineSkeletonComponent* GetSkeleton() const; + + /* Sets the cached SkeletonComponent */ + void SetSkeleton(USpineSkeletonComponent* InSkeleton); + + /* Called when the component is destroyed */ virtual void FinishDestroy() override; protected: @@ -99,4 +109,8 @@ protected: spine::Vector worldVertices; spine::SkeletonClipping clipper; + +private: + /* Cached skeleton to use to avoid Find functions on TICK */ + TWeakObjectPtr CachedSkeleton; };