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;