diff --git a/CHANGELOG.md b/CHANGELOG.md index ab9e67aba..11b2a97b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,8 @@ * Updated to Unreal Engine 4.20 (samples require 4.17+), see the `spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/SpinePlugin.build.cs` file on how to compile in 4.20 with the latest UBT API changes. * Updated to Unreal Engine 4.21 (samples require 4.21). * **Breaking change**: `UBoneDriverComponent` and `UBoneFollowerComponent` are now `USceneComponent` instead of `UActorComponent`. They either update only themselves, or also the owning `UActor`, depending on whether the new flag `UseComponentTransform` is set. See https://github.com/EsotericSoftware/spine-runtimes/pull/1175 +* Added query methods for slots, bones, skins and animations to `SpineSkeletonComponent` and `UTrackEntry`. These allow you to query these objects by name in both C++ and blueprints. +* Added `Preview Animation` and `Preview Skin` properties to `SpineSkeletonAnimationComponent`. Enter an animation or skin name to live-preview it in the editor. Enter an empty string to reset the animation or skin. ## C# ## * **Breaking changes** diff --git a/spine-cpp/README.md b/spine-cpp/README.md index f4f5a523e..abd5ce3b1 100644 --- a/spine-cpp/README.md +++ b/spine-cpp/README.md @@ -8,7 +8,7 @@ This spine Runtime may only be used for personal or internal use, typically to e The spine Runtimes are developed with the intent to be used with data exported from spine. By purchasing spine, `Section 2` of the [spine Software License](https://esotericsoftware.com/files/license.txt) grants the right to create and distribute derivative works of the spine Runtimes. -## spine version +## Spine version spine-cpp works with data exported from spine 3.7.xx. @@ -19,6 +19,9 @@ spine-cpp supports all spine features. 1. Download the spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it as a zip via the download button above. 2. Copy the contents of the `spine-cpp/spine-cpp/src` and `spine-cpp/spine-cpp/include` directories into your project. Be sure your header search is configured to find the contents of the `spine-cpp/spine-cpp/include` directory. Note that the includes use `spine/Xxx.h`, so the `spine` directory cannot be omitted when copying the files. +## Usage +### [Please see the spine-cpp guide for full documentation](http://esotericsoftware.com/spine-cpp) + ## Extension Extending spine-cpp requires implementing both the `SpineExtension` class and the TextureLoader class: diff --git a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp index 849d50649..c3cdcc172 100644 --- a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp +++ b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp @@ -171,6 +171,8 @@ void TrackEntry::reset() { _mixingFrom = NULL; _mixingTo = NULL; + setRendererObject(NULL); + _timelineMode.clear(); _timelineHoldMix.clear(); _timelinesRotation.clear(); diff --git a/spine-libgdx/spine-libgdx/pom.xml b/spine-libgdx/spine-libgdx/pom.xml index fc6f6b834..8a784273c 100644 --- a/spine-libgdx/spine-libgdx/pom.xml +++ b/spine-libgdx/spine-libgdx/pom.xml @@ -10,7 +10,7 @@ com.esotericsoftware.spine spine-libgdx jar - 3.7.83.2-SNAPSHOT + 3.7.92.1-SNAPSHOT spine-libgdx diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp index 75f49a7cc..40e638fd1 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpineEditorPlugin/Private/SpineSkeletonImportFactory.cpp @@ -81,9 +81,9 @@ UObject* USpineSkeletonAssetFactory::FactoryCreateFile (UClass * InClass, UObjec if (!FFileHelper::LoadFileToArray(rawData, *Filename, 0)) { return nullptr; } + asset->SetSkeletonDataFileName(FName(*Filename)); asset->SetRawData(rawData); - asset->SetSkeletonDataFileName(FName(*Filename)); const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName()); LoadAtlas(Filename, longPackagePath); return asset; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp index 8dabf4250..a9edb99dd 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp @@ -96,6 +96,17 @@ void USpineSkeletonAnimationComponent::InternalTick(float DeltaTime, bool CallDe CheckState(); if (state && bAutoPlaying) { + if (lastPreviewAnimation != PreviewAnimation) { + if (PreviewAnimation != "") SetAnimation(0, PreviewAnimation, true); + else SetEmptyAnimation(0, 0); + lastPreviewAnimation = PreviewAnimation; + } + + if (lastPreviewSkin != PreviewSkin) { + if (PreviewSkin != "") SetSkin(PreviewSkin); + else SetSkin("default"); + lastPreviewSkin = PreviewSkin; + } state->update(DeltaTime); state->apply(*skeleton); if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this); diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonComponent.cpp index c078910ae..5547ae43f 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonComponent.cpp @@ -42,7 +42,7 @@ USpineSkeletonComponent::USpineSkeletonComponent () { bAutoActivate = true; } -bool USpineSkeletonComponent::SetSkin(const FString& skinName) { +bool USpineSkeletonComponent::SetSkin (const FString skinName) { CheckState(); if (skeleton) { Skin* skin = skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName)); @@ -53,7 +53,24 @@ bool USpineSkeletonComponent::SetSkin(const FString& skinName) { else return false; } -bool USpineSkeletonComponent::SetAttachment (const FString& slotName, const FString& attachmentName) { +void USpineSkeletonComponent::GetSkins (TArray &Skins) { + CheckState(); + if (skeleton) { + for (size_t i = 0, n = skeleton->getData()->getSkins().size(); i < n; i++) { + Skins.Add(skeleton->getData()->getSkins()[i]->getName().buffer()); + } + } +} + +bool USpineSkeletonComponent::HasSkin (const FString skinName) { + CheckState(); + if (skeleton) { + return skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*skinName)) != nullptr; + } + return false; +} + +bool USpineSkeletonComponent::SetAttachment (const FString slotName, const FString attachmentName) { CheckState(); if (skeleton) { if (!skeleton->getAttachment(TCHAR_TO_UTF8(*slotName), TCHAR_TO_UTF8(*attachmentName))) return false; @@ -127,7 +144,7 @@ void USpineSkeletonComponent::SetBoneWorldPosition (const FString& BoneName, con } } -void USpineSkeletonComponent::UpdateWorldTransform() { +void USpineSkeletonComponent::UpdateWorldTransform () { CheckState(); if (skeleton) { skeleton->updateWorldTransform(); @@ -154,24 +171,24 @@ void USpineSkeletonComponent::SetScaleX (float scaleX) { if (skeleton) skeleton->setScaleX(scaleX); } -float USpineSkeletonComponent::GetScaleX() { +float USpineSkeletonComponent::GetScaleX () { CheckState(); if (skeleton) return skeleton->getScaleX(); return 1; } -void USpineSkeletonComponent::SetScaleY(float scaleY) { +void USpineSkeletonComponent::SetScaleY (float scaleY) { CheckState(); if (skeleton) skeleton->setScaleY(scaleY); } -float USpineSkeletonComponent::GetScaleY() { +float USpineSkeletonComponent::GetScaleY () { CheckState(); if (skeleton) return skeleton->getScaleY(); return 1; } -void USpineSkeletonComponent::GetBones(TArray &Bones) { +void USpineSkeletonComponent::GetBones (TArray &Bones) { CheckState(); if (skeleton) { for (size_t i = 0, n = skeleton->getBones().size(); i < n; i++) { @@ -180,6 +197,58 @@ void USpineSkeletonComponent::GetBones(TArray &Bones) { } } +bool USpineSkeletonComponent::HasBone (const FString BoneName) { + CheckState(); + if (skeleton) { + return skeleton->getData()->findBone(TCHAR_TO_UTF8(*BoneName)) != nullptr; + } + return false; +} + +void USpineSkeletonComponent::GetSlots (TArray &Slots) { + CheckState(); + if (skeleton) { + for (size_t i = 0, n = skeleton->getSlots().size(); i < n; i++) { + Slots.Add(skeleton->getSlots()[i]->getData().getName().buffer()); + } + } +} + +bool USpineSkeletonComponent::HasSlot (const FString SlotName) { + CheckState(); + if (skeleton) { + return skeleton->getData()->findSlot(TCHAR_TO_UTF8(*SlotName)) != nullptr; + } + return false; +} + +void USpineSkeletonComponent::GetAnimations(TArray &Animations) { + CheckState(); + if (skeleton) { + for (size_t i = 0, n = skeleton->getData()->getAnimations().size(); i < n; i++) { + Animations.Add(skeleton->getData()->getAnimations()[i]->getName().buffer()); + } + } +} + +bool USpineSkeletonComponent::HasAnimation(FString AnimationName) { + CheckState(); + if (skeleton) { + return skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*AnimationName)) != nullptr; + } + return false; +} + +float USpineSkeletonComponent::GetAnimationDuration(FString AnimationName) { + CheckState(); + if (skeleton) { + Animation *animation = skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*AnimationName)); + if (animation == nullptr) return 0; + else return animation->getDuration(); + } + return 0; +} + void USpineSkeletonComponent::BeginPlay() { Super::BeginPlay(); } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp index 5d1c378ee..8b101866f 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp @@ -76,6 +76,7 @@ void USpineSkeletonDataAsset::Serialize (FArchive& Ar) { Super::Serialize(Ar); if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData) importData = NewObject(this, TEXT("AssetImportData")); + LoadInfo(); } #endif @@ -92,13 +93,92 @@ void USpineSkeletonDataAsset::BeginDestroy () { Super::BeginDestroy(); } +class SP_API NullAttachmentLoader : public AttachmentLoader { +public: + + virtual RegionAttachment* newRegionAttachment(Skin& skin, const String& name, const String& path) { + return new(__FILE__, __LINE__) RegionAttachment(name); + } + + virtual MeshAttachment* newMeshAttachment(Skin& skin, const String& name, const String& path) { + return new(__FILE__, __LINE__) MeshAttachment(name); + } + + virtual BoundingBoxAttachment* newBoundingBoxAttachment(Skin& skin, const String& name) { + return new(__FILE__, __LINE__) BoundingBoxAttachment(name); + } + + virtual PathAttachment* newPathAttachment(Skin& skin, const String& name) { + return new(__FILE__, __LINE__) PathAttachment(name); + } + + virtual PointAttachment* newPointAttachment(Skin& skin, const String& name) { + return new(__FILE__, __LINE__) PointAttachment(name); + } + + virtual ClippingAttachment* newClippingAttachment(Skin& skin, const String& name) { + return new(__FILE__, __LINE__) ClippingAttachment(name); + } + + virtual void configureAttachment(Attachment* attachment) { + + } +}; + +void USpineSkeletonDataAsset::LoadInfo() { +#if WITH_EDITORONLY_DATA + int dataLen = rawData.Num(); + if (dataLen == 0) return; + NullAttachmentLoader loader; + SkeletonData* skeletonData = nullptr; + if (skeletonDataFileName.GetPlainNameString().Contains(TEXT(".json"))) { + SkeletonJson* json = new (__FILE__, __LINE__) SkeletonJson(&loader); + skeletonData = json->readSkeletonData((const char*)rawData.GetData()); + if (!skeletonData) { + FMessageDialog::Debugf(FText::FromString(UTF8_TO_TCHAR(json->getError().buffer()))); + UE_LOG(SpineLog, Error, TEXT("Couldn't load skeleton data and atlas: %s"), UTF8_TO_TCHAR(json->getError().buffer())); + } + delete json; + } else { + SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(&loader); + skeletonData = binary->readSkeletonData((const unsigned char*)rawData.GetData(), (int)rawData.Num()); + if (!skeletonData) { + FMessageDialog::Debugf(FText::FromString(UTF8_TO_TCHAR(binary->getError().buffer()))); + UE_LOG(SpineLog, Error, TEXT("Couldn't load skeleton data and atlas: %s"), UTF8_TO_TCHAR(binary->getError().buffer())); + } + delete binary; + } + if (skeletonData) { + Bones.Empty(); + for (int i = 0; i < skeletonData->getBones().size(); i++) + Bones.Add(UTF8_TO_TCHAR(skeletonData->getBones()[i]->getName().buffer())); + Skins.Empty(); + for (int i = 0; i < skeletonData->getSkins().size(); i++) + Skins.Add(UTF8_TO_TCHAR(skeletonData->getSkins()[i]->getName().buffer())); + Slots.Empty(); + for (int i = 0; i < skeletonData->getSlots().size(); i++) + Slots.Add(UTF8_TO_TCHAR(skeletonData->getSlots()[i]->getName().buffer())); + Animations.Empty(); + for (int i = 0; i < skeletonData->getAnimations().size(); i++) + Animations.Add(UTF8_TO_TCHAR(skeletonData->getAnimations()[i]->getName().buffer())); + Events.Empty(); + for (int i = 0; i < skeletonData->getEvents().size(); i++) + Events.Add(UTF8_TO_TCHAR(skeletonData->getEvents()[i]->getName().buffer())); + delete skeletonData; + } +#endif +} + void USpineSkeletonDataAsset::SetRawData(TArray &Data) { this->rawData.Empty(); this->rawData.Append(Data); + if (skeletonData) { delete skeletonData; skeletonData = nullptr; } + + LoadInfo(); } SkeletonData* USpineSkeletonDataAsset::GetSkeletonData (Atlas* Atlas) { diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp index 588f700eb..3410b7dd6 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp @@ -76,94 +76,100 @@ void USpineSkeletonRendererComponent::TickComponent (float DeltaTime, ELevelTick 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()->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()->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(); - } + 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]; + 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(); + } +} void USpineSkeletonRendererComponent::Flush (int &Idx, TArray &Vertices, TArray &Indices, TArray &Uvs, TArray &Colors, TArray& Colors2, UMaterialInstanceDynamic* Material) { if (Vertices.Num() == 0) return; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h index 303d11efc..f11ddd07f 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h @@ -90,7 +90,7 @@ public: UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry") bool GetLoop () { return entry ? entry->getLoop() : false; } UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry") - void SetLoop(bool loop) { if (entry) entry->setLoop(loop); } + void SetLoop(bool loop) { if (entry) entry->setLoop(loop); } UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry") float GetEventThreshold () { return entry ? entry->getEventThreshold() : 0; } @@ -157,6 +157,15 @@ public: UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry") void SetMixDuration(float mixDuration) { if (entry) entry->setMixDuration(mixDuration); } + UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry") + FString getAnimationName() { return entry ? entry->getAnimation()->getName().buffer() : ""; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry") + float getAnimationDuration() { return entry ? entry->getAnimation()->getDuration(): 0; } + + UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry") + float isValidAnimation() { return entry != nullptr; } + UPROPERTY(BlueprintAssignable, Category = "Components|Spine|TrackEntry") FSpineAnimationStartDelegate AnimationStart; @@ -250,6 +259,12 @@ public: UPROPERTY(BlueprintAssignable, Category="Components|Spine|Animation") FSpineAnimationDisposeDelegate AnimationDispose; + + UPROPERTY(Transient, EditAnywhere, Category=Spine) + FString PreviewAnimation; + + UPROPERTY(Transient, EditAnywhere, Category=Spine) + FString PreviewSkin; // used in C event callback. Needs to be public as we can't call // protected methods from plain old C function. @@ -270,4 +285,7 @@ private: /* If the animation should update automatically. */ UPROPERTY() bool bAutoPlaying; + + FString lastPreviewAnimation; + FString lastPreviewSkin; }; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h index 45a494579..376a8a93c 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h @@ -53,12 +53,18 @@ public: USpineSkeletonDataAsset* SkeletonData; spine::Skeleton* GetSkeleton () { return skeleton; }; + + UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton") + void GetSkins(TArray &Skins); UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton") - bool SetSkin (const FString& SkinName); + bool SetSkin (const FString SkinName); + + UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton") + bool HasSkin(const FString SkinName); UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton") - bool SetAttachment (const FString& slotName, const FString& attachmentName); + bool SetAttachment (const FString slotName, const FString attachmentName); UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton") FTransform GetBoneWorldTransform (const FString& BoneName); @@ -92,6 +98,24 @@ public: UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton") void GetBones(TArray &Bones); + + UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton") + bool HasBone(const FString BoneName); + + UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton") + void GetSlots(TArray &Slots); + + UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton") + bool HasSlot(const FString SlotName); + + UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton") + void GetAnimations(TArray &Animations); + + UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton") + bool HasAnimation(FString AnimationName); + + UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton") + float GetAnimationDuration(FString AnimationName); UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Skeleton") FSpineBeforeUpdateWorldTransformDelegate BeforeUpdateWorldTransform; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h index 442dc49b7..48dd58492 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h @@ -46,7 +46,7 @@ public: FString To; UPROPERTY(EditAnywhere, BlueprintReadWrite) - float Mix = 0; + float Mix = 0; }; UCLASS(BlueprintType, ClassGroup=(Spine)) @@ -70,6 +70,21 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray MixData; + + UPROPERTY(Transient, VisibleAnywhere) + TArray Bones; + + UPROPERTY(Transient, VisibleAnywhere) + TArray Slots; + + UPROPERTY(Transient, VisibleAnywhere) + TArray Skins; + + UPROPERTY(Transient, VisibleAnywhere) + TArray Animations; + + UPROPERTY(Transient, VisibleAnywhere) + TArray Events; protected: UPROPERTY() @@ -94,4 +109,6 @@ protected: virtual void GetAssetRegistryTags(TArray& OutTags) const override; virtual void Serialize (FArchive& Ar) override; #endif + + void LoadInfo(); }; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h index d91375605..ffe71baa8 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h @@ -47,6 +47,9 @@ public: virtual void TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; + /* Updates this skeleton renderer using the provided skeleton animation component. */ + void UpdateRenderer(USpineSkeletonComponent* Skeleton); + // Material Instance parents UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadOnly) UMaterialInterface* NormalBlendMaterial;