浏览代码

Merge branch '3.7' of https://github.com/esotericsoftware/spine-runtimes into 3.7

badlogic 6 年之前
父节点
当前提交
b0629e51ec

+ 2 - 0
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.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).
 * 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
 * **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# ##
 ## C# ##
 * **Breaking changes**
 * **Breaking changes**

+ 4 - 1
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.
 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.
 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.
 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.
 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
 ## Extension
 
 
 Extending spine-cpp requires implementing both the `SpineExtension` class and the TextureLoader class:
 Extending spine-cpp requires implementing both the `SpineExtension` class and the TextureLoader class:

+ 2 - 0
spine-cpp/spine-cpp/src/spine/AnimationState.cpp

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

+ 1 - 1
spine-libgdx/spine-libgdx/pom.xml

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

+ 1 - 1
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)) {
 	if (!FFileHelper::LoadFileToArray(rawData, *Filename, 0)) {
 		return nullptr;
 		return nullptr;
 	}
 	}
+	asset->SetSkeletonDataFileName(FName(*Filename));
 	asset->SetRawData(rawData);
 	asset->SetRawData(rawData);
 	
 	
-	asset->SetSkeletonDataFileName(FName(*Filename));
 	const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName());
 	const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName());
 	LoadAtlas(Filename, longPackagePath);
 	LoadAtlas(Filename, longPackagePath);
 	return asset;
 	return asset;

+ 11 - 0
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp

@@ -96,6 +96,17 @@ void USpineSkeletonAnimationComponent::InternalTick(float DeltaTime, bool CallDe
 	CheckState();
 	CheckState();
 
 
 	if (state && bAutoPlaying) {
 	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->update(DeltaTime);
 		state->apply(*skeleton);
 		state->apply(*skeleton);
 		if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this);
 		if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this);

+ 76 - 7
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonComponent.cpp

@@ -42,7 +42,7 @@ USpineSkeletonComponent::USpineSkeletonComponent () {
 	bAutoActivate = true;
 	bAutoActivate = true;
 }
 }
 
 
-bool USpineSkeletonComponent::SetSkin(const FString& skinName) {
+bool USpineSkeletonComponent::SetSkin (const FString skinName) {
 	CheckState();
 	CheckState();
 	if (skeleton) {
 	if (skeleton) {
 		Skin* skin = skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName));
 		Skin* skin = skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName));
@@ -53,7 +53,24 @@ bool USpineSkeletonComponent::SetSkin(const FString& skinName) {
 	else return false;
 	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();
 	CheckState();
 	if (skeleton) {
 	if (skeleton) {
 		if (!skeleton->getAttachment(TCHAR_TO_UTF8(*slotName), TCHAR_TO_UTF8(*attachmentName))) return false;
 		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();
 	CheckState();
 	if (skeleton) {
 	if (skeleton) {
 		skeleton->updateWorldTransform();
 		skeleton->updateWorldTransform();
@@ -154,24 +171,24 @@ void USpineSkeletonComponent::SetScaleX (float scaleX) {
 	if (skeleton) skeleton->setScaleX(scaleX);
 	if (skeleton) skeleton->setScaleX(scaleX);
 }
 }
 
 
-float USpineSkeletonComponent::GetScaleX() {
+float USpineSkeletonComponent::GetScaleX () {
 	CheckState();
 	CheckState();
 	if (skeleton) return skeleton->getScaleX();
 	if (skeleton) return skeleton->getScaleX();
 	return 1;
 	return 1;
 }
 }
 
 
-void USpineSkeletonComponent::SetScaleY(float scaleY) {
+void USpineSkeletonComponent::SetScaleY (float scaleY) {
 	CheckState();
 	CheckState();
 	if (skeleton) skeleton->setScaleY(scaleY);
 	if (skeleton) skeleton->setScaleY(scaleY);
 }
 }
 
 
-float USpineSkeletonComponent::GetScaleY() {
+float USpineSkeletonComponent::GetScaleY () {
 	CheckState();
 	CheckState();
 	if (skeleton) return skeleton->getScaleY();
 	if (skeleton) return skeleton->getScaleY();
 	return 1;
 	return 1;
 }
 }
 
 
-void USpineSkeletonComponent::GetBones(TArray<FString> &Bones) {
+void USpineSkeletonComponent::GetBones (TArray<FString> &Bones) {
 	CheckState();
 	CheckState();
 	if (skeleton) {
 	if (skeleton) {
 		for (size_t i = 0, n = skeleton->getBones().size(); i < n; i++) {
 		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() {
 void USpineSkeletonComponent::BeginPlay() {
 	Super::BeginPlay();
 	Super::BeginPlay();
 }
 }

+ 80 - 0
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp

@@ -76,6 +76,7 @@ void USpineSkeletonDataAsset::Serialize (FArchive& Ar) {
 	Super::Serialize(Ar);
 	Super::Serialize(Ar);
 	if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData)
 	if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData)
 		importData = NewObject<UAssetImportData>(this, TEXT("AssetImportData"));
 		importData = NewObject<UAssetImportData>(this, TEXT("AssetImportData"));
+	LoadInfo();
 }
 }
 
 
 #endif
 #endif
@@ -92,13 +93,92 @@ void USpineSkeletonDataAsset::BeginDestroy () {
 	Super::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) {
 void USpineSkeletonDataAsset::SetRawData(TArray<uint8> &Data) {
 	this->rawData.Empty();
 	this->rawData.Empty();
 	this->rawData.Append(Data);
 	this->rawData.Append(Data);
+
 	if (skeletonData) {
 	if (skeletonData) {
 		delete skeletonData;
 		delete skeletonData;
 		skeletonData = nullptr;
 		skeletonData = nullptr;
 	}
 	}
+
+	LoadInfo();
 }
 }
 
 
 SkeletonData* USpineSkeletonDataAsset::GetSkeletonData (Atlas* Atlas) {
 SkeletonData* USpineSkeletonDataAsset::GetSkeletonData (Atlas* Atlas) {

+ 88 - 82
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp

@@ -76,95 +76,101 @@ void USpineSkeletonRendererComponent::TickComponent (float DeltaTime, ELevelTick
 		UClass* skeletonClass = USpineSkeletonComponent::StaticClass();
 		UClass* skeletonClass = USpineSkeletonComponent::StaticClass();
 		USpineSkeletonComponent* skeleton = Cast<USpineSkeletonComponent>(owner->GetComponentByClass(skeletonClass));
 		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);					
+		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;
 				}
 				}
-			} 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]);
+				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();
 		}
 		}
+		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) {
 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;
 	if (Vertices.Num() == 0) return;
 	SetMaterial(Idx, Material);
 	SetMaterial(Idx, Material);

+ 19 - 1
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonAnimationComponent.h

@@ -90,7 +90,7 @@ public:
 	UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry")
 	UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry")
 	bool GetLoop () { return entry ? entry->getLoop() : false; }
 	bool GetLoop () { return entry ? entry->getLoop() : false; }
 	UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry")
 	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")
 	UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry")
 	float GetEventThreshold () { return entry ? entry->getEventThreshold() : 0; }
 	float GetEventThreshold () { return entry ? entry->getEventThreshold() : 0; }
@@ -157,6 +157,15 @@ public:
 	UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry")
 	UFUNCTION(BlueprintCallable, Category="Components|Spine|TrackEntry")
 	void SetMixDuration(float mixDuration) { if (entry) entry->setMixDuration(mixDuration); }
 	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")
 	UPROPERTY(BlueprintAssignable, Category = "Components|Spine|TrackEntry")
 	FSpineAnimationStartDelegate AnimationStart;
 	FSpineAnimationStartDelegate AnimationStart;
 
 
@@ -250,6 +259,12 @@ public:
 
 
 	UPROPERTY(BlueprintAssignable, Category="Components|Spine|Animation")
 	UPROPERTY(BlueprintAssignable, Category="Components|Spine|Animation")
 	FSpineAnimationDisposeDelegate AnimationDispose;
 	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
 	// used in C event callback. Needs to be public as we can't call
 	// protected methods from plain old C function.
 	// protected methods from plain old C function.
@@ -270,4 +285,7 @@ private:
 	/* If the animation should update automatically. */
 	/* If the animation should update automatically. */
 	UPROPERTY()
 	UPROPERTY()
 	bool bAutoPlaying;
 	bool bAutoPlaying;
+
+	FString lastPreviewAnimation;
+	FString lastPreviewSkin;
 };
 };

+ 26 - 2
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonComponent.h

@@ -53,12 +53,18 @@ public:
 	USpineSkeletonDataAsset* SkeletonData;
 	USpineSkeletonDataAsset* SkeletonData;
 	
 	
 	spine::Skeleton* GetSkeleton () { return skeleton; };
 	spine::Skeleton* GetSkeleton () { return skeleton; };
+
+	UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
+	void GetSkins(TArray<FString> &Skins);
 	
 	
 	UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
 	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")
 	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")
 	UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
 	FTransform GetBoneWorldTransform (const FString& BoneName);
 	FTransform GetBoneWorldTransform (const FString& BoneName);
@@ -92,6 +98,24 @@ public:
 
 
 	UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
 	UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
 	void GetBones(TArray<FString> &Bones);
 	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")
 	UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Skeleton")
 	FSpineBeforeUpdateWorldTransformDelegate BeforeUpdateWorldTransform;
 	FSpineBeforeUpdateWorldTransformDelegate BeforeUpdateWorldTransform;

+ 18 - 1
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonDataAsset.h

@@ -46,7 +46,7 @@ public:
 	FString To;
 	FString To;
 	
 	
 	UPROPERTY(EditAnywhere, BlueprintReadWrite)
 	UPROPERTY(EditAnywhere, BlueprintReadWrite)
-		float Mix = 0;
+	float Mix = 0;
 };
 };
 
 
 UCLASS(BlueprintType, ClassGroup=(Spine))
 UCLASS(BlueprintType, ClassGroup=(Spine))
@@ -70,6 +70,21 @@ public:
 
 
 	UPROPERTY(EditAnywhere, BlueprintReadWrite)
 	UPROPERTY(EditAnywhere, BlueprintReadWrite)
 	TArray<FSpineAnimationStateMixData> MixData;
 	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:
 protected:
 	UPROPERTY()
 	UPROPERTY()
@@ -94,4 +109,6 @@ protected:
 	virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override;
 	virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override;
 	virtual void Serialize (FArchive& Ar) override;
 	virtual void Serialize (FArchive& Ar) override;
 #endif
 #endif
+
+	void LoadInfo();
 };
 };

+ 3 - 0
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineSkeletonRendererComponent.h

@@ -47,6 +47,9 @@ public:
 		
 		
 	virtual void TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
 	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
 	// Material Instance parents
 	UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadOnly)
 	UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadOnly)
 	UMaterialInterface* NormalBlendMaterial;
 	UMaterialInterface* NormalBlendMaterial;