This commit is contained in:
badlogic 2019-03-07 19:00:52 +01:00
commit b0629e51ec
13 changed files with 334 additions and 99 deletions

View File

@ -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**

View File

@ -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:

View File

@ -171,6 +171,8 @@ void TrackEntry::reset() {
_mixingFrom = NULL;
_mixingTo = NULL;
setRendererObject(NULL);
_timelineMode.clear();
_timelineHoldMix.clear();
_timelinesRotation.clear();

View File

@ -10,7 +10,7 @@
<groupId>com.esotericsoftware.spine</groupId>
<artifactId>spine-libgdx</artifactId>
<packaging>jar</packaging>
<version>3.7.83.2-SNAPSHOT</version>
<version>3.7.92.1-SNAPSHOT</version>
<name>spine-libgdx</name>

View File

@ -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;

View File

@ -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);

View File

@ -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<FString> &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<FString> &Bones) {
void USpineSkeletonComponent::GetBones (TArray<FString> &Bones) {
CheckState();
if (skeleton) {
for (size_t i = 0, n = skeleton->getBones().size(); i < n; i++) {
@ -180,6 +197,58 @@ void USpineSkeletonComponent::GetBones(TArray<FString> &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<FString> &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<FString> &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();
}

View File

@ -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<UAssetImportData>(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<uint8> &Data) {
this->rawData.Empty();
this->rawData.Append(Data);
if (skeletonData) {
delete skeletonData;
skeletonData = nullptr;
}
LoadInfo();
}
SkeletonData* USpineSkeletonDataAsset::GetSkeletonData (Atlas* Atlas) {

View File

@ -76,94 +76,100 @@ void USpineSkeletonRendererComponent::TickComponent (float DeltaTime, ELevelTick
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()) {
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<FVector> &Vertices, TArray<int32> &Indices, TArray<FVector2D> &Uvs, TArray<FColor> &Colors, TArray<FVector>& Colors2, UMaterialInstanceDynamic* Material) {
if (Vertices.Num() == 0) return;

View File

@ -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;
@ -251,6 +260,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.
void GCTrackEntry(UTrackEntry* entry) { trackEntries.Remove(entry); }
@ -270,4 +285,7 @@ private:
/* If the animation should update automatically. */
UPROPERTY()
bool bAutoPlaying;
FString lastPreviewAnimation;
FString lastPreviewSkin;
};

View File

@ -54,11 +54,17 @@ public:
spine::Skeleton* GetSkeleton () { return skeleton; };
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
bool SetSkin (const FString& SkinName);
UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
void GetSkins(TArray<FString> &Skins);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
bool SetAttachment (const FString& slotName, const FString& attachmentName);
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);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
FTransform GetBoneWorldTransform (const FString& BoneName);
@ -93,6 +99,24 @@ public:
UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
void GetBones(TArray<FString> &Bones);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
bool HasBone(const FString BoneName);
UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
void GetSlots(TArray<FString> &Slots);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
bool HasSlot(const FString SlotName);
UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
void GetAnimations(TArray<FString> &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;

View File

@ -46,7 +46,7 @@ public:
FString To;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Mix = 0;
float Mix = 0;
};
UCLASS(BlueprintType, ClassGroup=(Spine))
@ -71,6 +71,21 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FSpineAnimationStateMixData> MixData;
UPROPERTY(Transient, VisibleAnywhere)
TArray<FString> Bones;
UPROPERTY(Transient, VisibleAnywhere)
TArray<FString> Slots;
UPROPERTY(Transient, VisibleAnywhere)
TArray<FString> Skins;
UPROPERTY(Transient, VisibleAnywhere)
TArray<FString> Animations;
UPROPERTY(Transient, VisibleAnywhere)
TArray<FString> Events;
protected:
UPROPERTY()
TArray<uint8> rawData;
@ -94,4 +109,6 @@ protected:
virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override;
virtual void Serialize (FArchive& Ar) override;
#endif
void LoadInfo();
};

View File

@ -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;