mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
- (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.
This commit is contained in:
parent
e95fca696f
commit
b21ecba445
@ -94,7 +94,7 @@ void USpineSkeletonAnimationComponent::TickComponent(float DeltaTime, ELevelTick
|
|||||||
void USpineSkeletonAnimationComponent::InternalTick(float DeltaTime, bool CallDelegates) {
|
void USpineSkeletonAnimationComponent::InternalTick(float DeltaTime, bool CallDelegates) {
|
||||||
CheckState();
|
CheckState();
|
||||||
|
|
||||||
if (state) {
|
if (state && bAutoPlaying) {
|
||||||
state->update(DeltaTime);
|
state->update(DeltaTime);
|
||||||
state->apply(*skeleton);
|
state->apply(*skeleton);
|
||||||
if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this);
|
if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this);
|
||||||
@ -144,6 +144,36 @@ void USpineSkeletonAnimationComponent::FinishDestroy () {
|
|||||||
Super::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) {
|
void USpineSkeletonAnimationComponent::SetTimeScale(float timeScale) {
|
||||||
CheckState();
|
CheckState();
|
||||||
if (state) state->setTimeScale(timeScale);
|
if (state) state->setTimeScale(timeScale);
|
||||||
|
|||||||
@ -68,100 +68,123 @@ void USpineSkeletonRendererComponent::BeginPlay () {
|
|||||||
Super::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) {
|
void USpineSkeletonRendererComponent::TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) {
|
||||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||||
|
|
||||||
AActor* owner = GetOwner();
|
|
||||||
if (owner) {
|
|
||||||
UClass* skeletonClass = USpineSkeletonComponent::StaticClass();
|
|
||||||
USpineSkeletonComponent* skeleton = Cast<USpineSkeletonComponent>(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()) {
|
//Check if we need to initialize the skeleton cache, this will help us to avoid excessive Find functions when ticking, thus improving performance
|
||||||
atlasNormalBlendMaterials.SetNum(0);
|
if (!GetSkeleton())
|
||||||
pageToNormalBlendMaterial.Empty();
|
{
|
||||||
atlasAdditiveBlendMaterials.SetNum(0);
|
USpineSkeletonComponent* FoundSkeletonComponent = GetOwner()->FindComponentByClass<USpineSkeletonComponent>();
|
||||||
pageToAdditiveBlendMaterial.Empty();
|
if (FoundSkeletonComponent)
|
||||||
atlasMultiplyBlendMaterials.SetNum(0);
|
{
|
||||||
pageToMultiplyBlendMaterial.Empty();
|
SetSkeleton(FoundSkeletonComponent);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -194,6 +194,16 @@ public:
|
|||||||
virtual void TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
virtual void TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||||
|
|
||||||
virtual void FinishDestroy () 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
|
// Blueprint functions
|
||||||
UFUNCTION(BlueprintCallable, Category="Components|Spine|Animation")
|
UFUNCTION(BlueprintCallable, Category="Components|Spine|Animation")
|
||||||
@ -255,4 +265,9 @@ protected:
|
|||||||
// in transit within a blueprint
|
// in transit within a blueprint
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TSet<UTrackEntry*> trackEntries;
|
TSet<UTrackEntry*> trackEntries;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* If the animation should update automatically. */
|
||||||
|
UPROPERTY()
|
||||||
|
bool bAutoPlaying;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -86,10 +86,20 @@ public:
|
|||||||
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
|
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
|
||||||
FLinearColor Color = FLinearColor(1, 1, 1, 1);
|
FLinearColor Color = FLinearColor(1, 1, 1, 1);
|
||||||
|
|
||||||
/** Whether to generate collision geometry for the skeleton, or not. */
|
/** Whether to generate collision geometry for the skeleton, or not. */
|
||||||
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
|
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
|
||||||
bool bCreateCollision;
|
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;
|
virtual void FinishDestroy() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -99,4 +109,8 @@ protected:
|
|||||||
|
|
||||||
spine::Vector<float> worldVertices;
|
spine::Vector<float> worldVertices;
|
||||||
spine::SkeletonClipping clipper;
|
spine::SkeletonClipping clipper;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Cached skeleton to use to avoid Find functions on TICK */
|
||||||
|
TWeakObjectPtr<USpineSkeletonComponent> CachedSkeleton;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user