接入Spine

This commit is contained in:
2025-09-23 02:52:33 +08:00
parent 89d751ac34
commit c90b46f430
291 changed files with 32686 additions and 473 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,143 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineAtlasImportFactory.h"
#include "AssetToolsModule.h"
#include "SpineAtlasAsset.h"
#include "Editor.h"
#define LOCTEXT_NAMESPACE "Spine"
using namespace spine;
USpineAtlasAssetFactory::USpineAtlasAssetFactory(const FObjectInitializer &objectInitializer) : Super(objectInitializer) {
bCreateNew = false;
bEditAfterNew = true;
bEditorImport = true;
SupportedClass = USpineAtlasAsset::StaticClass();
Formats.Add(TEXT("atlas;Spine Atlas file"));
}
FText USpineAtlasAssetFactory::GetToolTip() const {
return LOCTEXT("SpineAtlasAssetFactory", "Animations exported from Spine");
}
bool USpineAtlasAssetFactory::FactoryCanImport(const FString &Filename) {
return true;
}
UObject *USpineAtlasAssetFactory::FactoryCreateFile(UClass *InClass, UObject *InParent, FName InName, EObjectFlags Flags, const FString &Filename, const TCHAR *Parms, FFeedbackContext *Warn, bool &bOutOperationCanceled) {
FString FileExtension = FPaths::GetExtension(Filename);
GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPreImport(this, InClass, InParent, InName, *FileExtension);
FString rawString;
if (!FFileHelper::LoadFileToString(rawString, *Filename)) {
return nullptr;
}
FString currentSourcePath, filenameNoExtension, unusedExtension;
const FString longPackagePath = FPackageName::GetLongPackagePath(InParent->GetOutermost()->GetPathName());
FPaths::Split(UFactory::GetCurrentFilename(), currentSourcePath, filenameNoExtension, unusedExtension);
USpineAtlasAsset *asset = NewObject<USpineAtlasAsset>(InParent, InClass, InName, Flags);
asset->SetRawData(rawString);
asset->SetAtlasFileName(FName(*Filename));
LoadAtlas(asset, currentSourcePath, longPackagePath);
GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetPostImport(this, asset);
return asset;
}
bool USpineAtlasAssetFactory::CanReimport(UObject *Obj, TArray<FString> &OutFilenames) {
USpineAtlasAsset *asset = Cast<USpineAtlasAsset>(Obj);
if (!asset) return false;
FString filename = asset->GetAtlasFileName().ToString();
if (!filename.IsEmpty())
OutFilenames.Add(filename);
return true;
}
void USpineAtlasAssetFactory::SetReimportPaths(UObject *Obj, const TArray<FString> &NewReimportPaths) {
USpineAtlasAsset *asset = Cast<USpineAtlasAsset>(Obj);
if (asset && ensure(NewReimportPaths.Num() == 1))
asset->SetAtlasFileName(FName(*NewReimportPaths[0]));
}
EReimportResult::Type USpineAtlasAssetFactory::Reimport(UObject *Obj) {
USpineAtlasAsset *asset = Cast<USpineAtlasAsset>(Obj);
FString rawString;
if (!FFileHelper::LoadFileToString(rawString, *asset->GetAtlasFileName().ToString())) return EReimportResult::Failed;
asset->SetRawData(rawString);
FString currentSourcePath, filenameNoExtension, unusedExtension;
const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName());
FString currentFileName = asset->GetAtlasFileName().ToString();
FPaths::Split(currentFileName, currentSourcePath, filenameNoExtension, unusedExtension);
LoadAtlas(asset, currentSourcePath, longPackagePath);
if (Obj->GetOuter()) Obj->GetOuter()->MarkPackageDirty();
else
Obj->MarkPackageDirty();
GEditor->GetEditorSubsystem<UImportSubsystem>()->BroadcastAssetReimport(asset);
return EReimportResult::Succeeded;
}
UTexture2D *resolveTexture(USpineAtlasAsset *Asset, const FString &PageFileName, const FString &TargetSubPath) {
FAssetToolsModule &AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
TArray<FString> fileNames;
fileNames.Add(PageFileName);
TArray<UObject *> importedAsset = AssetToolsModule.Get().ImportAssets(fileNames, TargetSubPath);
UTexture2D *texture = (importedAsset.Num() > 0) ? Cast<UTexture2D>(importedAsset[0]) : nullptr;
return texture;
}
void USpineAtlasAssetFactory::LoadAtlas(USpineAtlasAsset *Asset, const FString &CurrentSourcePath, const FString &LongPackagePath) {
Atlas *atlas = Asset->GetAtlas();
Asset->atlasPages.Empty();
const FString targetTexturePath = LongPackagePath / TEXT("Textures");
Vector<AtlasPage *> &pages = atlas->getPages();
for (size_t i = 0, n = pages.size(); i < n; i++) {
AtlasPage *page = pages[i];
const FString sourceTextureFilename = FPaths::Combine(*CurrentSourcePath, UTF8_TO_TCHAR(page->name.buffer()));
UTexture2D *texture = resolveTexture(Asset, sourceTextureFilename, targetTexturePath);
Asset->atlasPages.Add(texture);
}
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,71 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineEditorPlugin.h"
#include "AssetTypeActions_Base.h"
#include "SpineAtlasAsset.h"
#include "SpineSkeletonDataAsset.h"
class FSpineAtlasAssetTypeActions : public FAssetTypeActions_Base {
public:
UClass *GetSupportedClass() const override { return USpineAtlasAsset::StaticClass(); };
FText GetName() const override { return INVTEXT("Spine atlas asset"); };
FColor GetTypeColor() const override { return FColor::Red; };
uint32 GetCategories() override { return EAssetTypeCategories::Misc; };
};
class FSpineSkeletonDataAssetTypeActions : public FAssetTypeActions_Base {
public:
UClass *GetSupportedClass() const override { return USpineSkeletonDataAsset::StaticClass(); };
FText GetName() const override { return INVTEXT("Spine data asset"); };
FColor GetTypeColor() const override { return FColor::Red; };
uint32 GetCategories() override { return EAssetTypeCategories::Misc; };
};
class FSpineEditorPlugin : public ISpineEditorPlugin {
virtual void StartupModule() override;
virtual void ShutdownModule() override;
TSharedPtr<FSpineAtlasAssetTypeActions> SpineAtlasAssetTypeActions;
TSharedPtr<FSpineSkeletonDataAssetTypeActions> SpineSkeletonDataAssetTypeActions;
};
IMPLEMENT_MODULE(FSpineEditorPlugin, SpineEditorPlugin)
void FSpineEditorPlugin::StartupModule() {
SpineAtlasAssetTypeActions = MakeShared<FSpineAtlasAssetTypeActions>();
FAssetToolsModule::GetModule().Get().RegisterAssetTypeActions(SpineAtlasAssetTypeActions.ToSharedRef());
SpineSkeletonDataAssetTypeActions = MakeShared<FSpineSkeletonDataAssetTypeActions>();
FAssetToolsModule::GetModule().Get().RegisterAssetTypeActions(SpineSkeletonDataAssetTypeActions.ToSharedRef());
}
void FSpineEditorPlugin::ShutdownModule() {
if (!FModuleManager::Get().IsModuleLoaded("AssetTools")) return;
FAssetToolsModule::GetModule().Get().UnregisterAssetTypeActions(SpineAtlasAssetTypeActions.ToSharedRef());
FAssetToolsModule::GetModule().Get().UnregisterAssetTypeActions(SpineSkeletonDataAssetTypeActions.ToSharedRef());
}

View File

@ -0,0 +1,126 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineSkeletonImportFactory.h"
#include "AssetToolsModule.h"
#include "Developer/AssetTools/Public/IAssetTools.h"
#include "SpineSkeletonDataAsset.h"
#include <string.h>
#define LOCTEXT_NAMESPACE "Spine"
USpineSkeletonAssetFactory::USpineSkeletonAssetFactory(const FObjectInitializer &objectInitializer) : Super(objectInitializer) {
bCreateNew = false;
bEditAfterNew = true;
bEditorImport = true;
SupportedClass = USpineSkeletonDataAsset::StaticClass();
Formats.Add(TEXT("json;Spine skeleton file"));
Formats.Add(TEXT("skel;Spine skeleton file"));
}
FText USpineSkeletonAssetFactory::GetToolTip() const {
return LOCTEXT("USpineSkeletonAssetFactory", "Animations exported from Spine");
}
bool USpineSkeletonAssetFactory::FactoryCanImport(const FString &Filename) {
if (Filename.Contains(TEXT(".skel"))) return true;
if (Filename.Contains(TEXT(".json"))) {
TArray<uint8> rawData;
if (!FFileHelper::LoadFileToArray(rawData, *Filename, 0)) {
return false;
}
if (rawData.Num() == 0) return false;
return strcmp((const char *) rawData.GetData(), "skeleton") > 0 && strcmp((const char *) rawData.GetData(), "spine") > 0;
}
return false;
}
void LoadAtlas(const FString &Filename, const FString &TargetPath) {
FAssetToolsModule &AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
FString skelFile = Filename.Replace(TEXT(".skel"), TEXT(".atlas")).Replace(TEXT(".json"), TEXT(".atlas"));
if (!FPaths::FileExists(skelFile)) return;
TArray<FString> fileNames;
fileNames.Add(skelFile);
AssetToolsModule.Get().ImportAssets(fileNames, TargetPath);
}
UObject *USpineSkeletonAssetFactory::FactoryCreateFile(UClass *InClass, UObject *InParent, FName InName, EObjectFlags Flags, const FString &Filename, const TCHAR *Parms, FFeedbackContext *Warn, bool &bOutOperationCanceled) {
USpineSkeletonDataAsset *asset = NewObject<USpineSkeletonDataAsset>(InParent, InClass, InName, Flags);
TArray<uint8> rawData;
if (!FFileHelper::LoadFileToArray(rawData, *Filename, 0)) {
return nullptr;
}
asset->SetSkeletonDataFileName(FName(*Filename));
asset->SetRawData(rawData);
const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName());
LoadAtlas(Filename, longPackagePath);
return asset;
}
bool USpineSkeletonAssetFactory::CanReimport(UObject *Obj, TArray<FString> &OutFilenames) {
USpineSkeletonDataAsset *asset = Cast<USpineSkeletonDataAsset>(Obj);
if (!asset) return false;
FString filename = asset->GetSkeletonDataFileName().ToString();
if (!filename.IsEmpty())
OutFilenames.Add(filename);
return true;
}
void USpineSkeletonAssetFactory::SetReimportPaths(UObject *Obj, const TArray<FString> &NewReimportPaths) {
USpineSkeletonDataAsset *asset = Cast<USpineSkeletonDataAsset>(Obj);
if (asset && ensure(NewReimportPaths.Num() == 1))
asset->SetSkeletonDataFileName(FName(*NewReimportPaths[0]));
}
EReimportResult::Type USpineSkeletonAssetFactory::Reimport(UObject *Obj) {
USpineSkeletonDataAsset *asset = Cast<USpineSkeletonDataAsset>(Obj);
TArray<uint8> rawData;
if (!FFileHelper::LoadFileToArray(rawData, *asset->GetSkeletonDataFileName().ToString(), 0)) return EReimportResult::Failed;
asset->SetRawData(rawData);
const FString longPackagePath = FPackageName::GetLongPackagePath(asset->GetOutermost()->GetPathName());
LoadAtlas(*asset->GetSkeletonDataFileName().ToString(), longPackagePath);
if (Obj->GetOuter()) Obj->GetOuter()->MarkPackageDirty();
else
Obj->MarkPackageDirty();
return EReimportResult::Succeeded;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,52 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
// clang-format off
#include "SpineAtlasAsset.h"
#include "UnrealEd.h"
#include "SpineAtlasImportFactory.generated.h"
// clang-format on
UCLASS()
class USpineAtlasAssetFactory : public UFactory, public FReimportHandler {
GENERATED_UCLASS_BODY()
virtual FText GetToolTip() const override;
virtual bool FactoryCanImport(const FString &Filename) override;
virtual UObject *FactoryCreateFile(UClass *InClass, UObject *InParent, FName InName, EObjectFlags Flags, const FString &Filename, const TCHAR *Parms, FFeedbackContext *Warn, bool &bOutOperationCanceled) override;
virtual bool CanReimport(UObject *Obj, TArray<FString> &OutFilenames) override;
virtual void SetReimportPaths(UObject *Obj, const TArray<FString> &NewReimportPaths) override;
virtual EReimportResult::Type Reimport(UObject *Obj) override;
void LoadAtlas(USpineAtlasAsset *Asset, const FString &CurrentSourcePath, const FString &LongPackagePath);
};

View File

@ -0,0 +1,44 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
#include "Modules/ModuleManager.h"
class ISpineEditorPlugin : public IModuleInterface {
public:
static inline ISpineEditorPlugin &Get() {
return FModuleManager::LoadModuleChecked<ISpineEditorPlugin>("SpineEditorPlugin");
}
static inline bool IsAvailable() {
return FModuleManager::Get().IsModuleLoaded("SpineEditorPlugin");
}
};

View File

@ -0,0 +1,49 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
// clang-format off
#include "SpineAtlasAsset.h"
#include "UnrealEd.h"
#include "SpineSkeletonImportFactory.generated.h"
// clang-format on
UCLASS()
class USpineSkeletonAssetFactory : public UFactory, public FReimportHandler {
GENERATED_UCLASS_BODY()
virtual FText GetToolTip() const override;
virtual bool FactoryCanImport(const FString &Filename) override;
virtual UObject *FactoryCreateFile(UClass *InClass, UObject *InParent, FName InName, EObjectFlags Flags, const FString &Filename, const TCHAR *Parms, FFeedbackContext *Warn, bool &bOutOperationCanceled) override;
virtual bool CanReimport(UObject *Obj, TArray<FString> &OutFilenames) override;
virtual void SetReimportPaths(UObject *Obj, const TArray<FString> &NewReimportPaths) override;
virtual EReimportResult::Type Reimport(UObject *Obj) override;
};

View File

@ -0,0 +1,36 @@
using System.IO;
namespace UnrealBuildTool.Rules
{
public class SpineEditorPlugin : ModuleRules
{
public SpineEditorPlugin(ReadOnlyTargetRules target) : base(target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public"));
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "../SpinePlugin/Public/spine-cpp/include"));
PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private"));
PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "../SpinePlugin/Public/spine-cpp/include"));
PublicDependencyModuleNames.AddRange(new [] {
"Core",
"CoreUObject",
"Engine",
"UnrealEd",
"SpinePlugin"
});
PublicIncludePathModuleNames.AddRange(new [] {
"AssetTools",
"AssetRegistry"
});
DynamicallyLoadedModuleNames.AddRange(new [] {
"AssetTools",
"AssetRegistry"
});
}
}
}

View File

@ -0,0 +1,415 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SSpineWidget.h"
#include "Framework/Application/SlateApplication.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Materials/MaterialInterface.h"
#include "Rendering/DrawElements.h"
#include "Runtime/SlateRHIRenderer/Public/Interfaces/ISlateRHIRendererModule.h"
#include "Slate/SMeshWidget.h"
#include "Slate/SlateVectorArtData.h"
#include "SlateMaterialBrush.h"
#include "SpineWidget.h"
#include <spine/spine.h>
using namespace spine;
static int brushNameId = 0;
// Workaround for https://github.com/EsotericSoftware/spine-runtimes/issues/1458
// See issue comments for more information.
struct SpineSlateMaterialBrush : public FSlateBrush {
static TArray<FName> NamePool;
static FCriticalSection NamePoolLock;
SpineSlateMaterialBrush(class UMaterialInterface &InMaterial, const FVector2D &InImageSize)
: FSlateBrush(ESlateBrushDrawType::Image, FName(TEXT("None")), FMargin(0), ESlateBrushTileType::NoTile, ESlateBrushImageType::FullColor, InImageSize, FLinearColor::White, &InMaterial) {
// Workaround for https://github.com/EsotericSoftware/spine-runtimes/issues/2006
FScopeLock Lock(&NamePoolLock);
if (NamePool.Num() > 0) {
ResourceName = NamePool.Pop(false);
} else {
static uint32 NextId = 0;
FString brushName = TEXT("SpineSlateMatBrush");
brushName.AppendInt(NextId++);
ResourceName = FName(*brushName);
}
}
~SpineSlateMaterialBrush() {
FScopeLock Lock(&NamePoolLock);
NamePool.Add(ResourceName);
}
};
TArray<FName> SpineSlateMaterialBrush::NamePool;
FCriticalSection SpineSlateMaterialBrush::NamePoolLock;
void SSpineWidget::Construct(const FArguments &args) {
}
void SSpineWidget::SetData(USpineWidget *Widget) {
this->widget = Widget;
if (widget && widget->skeleton && widget->Atlas) {
Skeleton *skeleton = widget->skeleton;
skeleton->setToSetupPose();
skeleton->updateWorldTransform(Physics_None);
Vector<float> scratchBuffer;
float x, y, w, h;
skeleton->getBounds(x, y, w, h, scratchBuffer);
boundsMin.X = x;
boundsMin.Y = y;
boundsSize.X = w;
boundsSize.Y = h;
}
}
static void setVertex(FSlateVertex *vertex, float x, float y, float u, float v, const FColor &color, const FVector2D &offset) {
vertex->Position.X = offset.X + x;
vertex->Position.Y = offset.Y + y;
vertex->TexCoords[0] = u;
vertex->TexCoords[1] = v;
vertex->TexCoords[2] = u;
vertex->TexCoords[3] = v;
vertex->MaterialTexCoords.X = u;
vertex->MaterialTexCoords.Y = v;
vertex->Color = color;
vertex->PixelSize[0] = 1;
vertex->PixelSize[1] = 1;
}
int32 SSpineWidget::OnPaint(const FPaintArgs &Args, const FGeometry &AllottedGeometry, const FSlateRect &MyClippingRect, FSlateWindowElementList &OutDrawElements,
int32 LayerId, const FWidgetStyle &InWidgetStyle, bool bParentEnabled) const {
SSpineWidget *self = (SSpineWidget *) this;
UMaterialInstanceDynamic *MatNow = nullptr;
if (widget && widget->skeleton && widget->Atlas) {
widget->skeleton->getColor().set(widget->Color.R, widget->Color.G, widget->Color.B, widget->Color.A);
if (widget->atlasNormalBlendMaterials.Num() != widget->Atlas->atlasPages.Num()) {
widget->atlasNormalBlendMaterials.SetNum(0);
widget->pageToNormalBlendMaterial.Empty();
widget->atlasAdditiveBlendMaterials.SetNum(0);
widget->pageToAdditiveBlendMaterial.Empty();
widget->atlasMultiplyBlendMaterials.SetNum(0);
widget->pageToMultiplyBlendMaterial.Empty();
widget->atlasScreenBlendMaterials.SetNum(0);
widget->pageToScreenBlendMaterial.Empty();
for (int i = 0; i < widget->Atlas->atlasPages.Num(); i++) {
AtlasPage *currPage = widget->Atlas->GetAtlas()->getPages()[i];
UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(widget->NormalBlendMaterial, widget);
material->SetTextureParameterValue(widget->TextureParameterName, widget->Atlas->atlasPages[i]);
widget->atlasNormalBlendMaterials.Add(material);
widget->pageToNormalBlendMaterial.Add(currPage, material);
material = UMaterialInstanceDynamic::Create(widget->AdditiveBlendMaterial, widget);
material->SetTextureParameterValue(widget->TextureParameterName, widget->Atlas->atlasPages[i]);
widget->atlasAdditiveBlendMaterials.Add(material);
widget->pageToAdditiveBlendMaterial.Add(currPage, material);
material = UMaterialInstanceDynamic::Create(widget->MultiplyBlendMaterial, widget);
material->SetTextureParameterValue(widget->TextureParameterName, widget->Atlas->atlasPages[i]);
widget->atlasMultiplyBlendMaterials.Add(material);
widget->pageToMultiplyBlendMaterial.Add(currPage, material);
material = UMaterialInstanceDynamic::Create(widget->ScreenBlendMaterial, widget);
material->SetTextureParameterValue(widget->TextureParameterName, widget->Atlas->atlasPages[i]);
widget->atlasScreenBlendMaterials.Add(material);
widget->pageToScreenBlendMaterial.Add(currPage, material);
}
} else {
widget->pageToNormalBlendMaterial.Empty();
widget->pageToAdditiveBlendMaterial.Empty();
widget->pageToMultiplyBlendMaterial.Empty();
widget->pageToScreenBlendMaterial.Empty();
for (int i = 0; i < widget->Atlas->atlasPages.Num(); i++) {
AtlasPage *currPage = widget->Atlas->GetAtlas()->getPages()[i];
UTexture2D *texture = widget->Atlas->atlasPages[i];
UTexture *oldTexture = nullptr;
UMaterialInstanceDynamic *current = widget->atlasNormalBlendMaterials[i];
if (!current || !current->GetTextureParameterValue(widget->TextureParameterName, oldTexture) || oldTexture != texture) {
UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(widget->NormalBlendMaterial, widget);
material->SetTextureParameterValue(widget->TextureParameterName, texture);
widget->atlasNormalBlendMaterials[i] = material;
}
widget->pageToNormalBlendMaterial.Add(currPage, widget->atlasNormalBlendMaterials[i]);
current = widget->atlasAdditiveBlendMaterials[i];
if (!current || !current->GetTextureParameterValue(widget->TextureParameterName, oldTexture) || oldTexture != texture) {
UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(widget->AdditiveBlendMaterial, widget);
material->SetTextureParameterValue(widget->TextureParameterName, texture);
widget->atlasAdditiveBlendMaterials[i] = material;
}
widget->pageToAdditiveBlendMaterial.Add(currPage, widget->atlasAdditiveBlendMaterials[i]);
current = widget->atlasMultiplyBlendMaterials[i];
if (!current || !current->GetTextureParameterValue(widget->TextureParameterName, oldTexture) || oldTexture != texture) {
UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(widget->MultiplyBlendMaterial, widget);
material->SetTextureParameterValue(widget->TextureParameterName, texture);
widget->atlasMultiplyBlendMaterials[i] = material;
}
widget->pageToMultiplyBlendMaterial.Add(currPage, widget->atlasMultiplyBlendMaterials[i]);
current = widget->atlasScreenBlendMaterials[i];
if (!current || !current->GetTextureParameterValue(widget->TextureParameterName, oldTexture) || oldTexture != texture) {
UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(widget->ScreenBlendMaterial, widget);
material->SetTextureParameterValue(widget->TextureParameterName, texture);
widget->atlasScreenBlendMaterials[i] = material;
}
widget->pageToScreenBlendMaterial.Add(currPage, widget->atlasScreenBlendMaterials[i]);
}
}
self->UpdateMesh(LayerId, OutDrawElements, AllottedGeometry, widget->skeleton);
}
return LayerId;
}
void SSpineWidget::Flush(int32 LayerId, FSlateWindowElementList &OutDrawElements, const FGeometry &AllottedGeometry, int &Idx, TArray<FVector> &Vertices, TArray<int32> &Indices, TArray<FVector2D> &Uvs, TArray<FColor> &Colors, TArray<FVector> &Colors2, UMaterialInstanceDynamic *Material) {
if (Vertices.Num() == 0) return;
SSpineWidget *self = (SSpineWidget *) this;
const FVector2D widgetSize = AllottedGeometry.GetLocalSize();
const FVector2D sizeScale = widgetSize / FVector2D(boundsSize.X, boundsSize.Y);
const float setupScale = sizeScale.GetMin();
for (int i = 0; i < Vertices.Num(); i++) {
Vertices[i] = (Vertices[i] + FVector(-boundsMin.X - boundsSize.X / 2, boundsMin.Y + boundsSize.Y / 2, 0)) * setupScale + FVector(widgetSize.X / 2, widgetSize.Y / 2, 0);
}
self->renderData.IndexData.SetNumUninitialized(Indices.Num());
SlateIndex *indexData = (SlateIndex *) renderData.IndexData.GetData();
for (int i = 0; i < Indices.Num(); i++) {
indexData[i] = (SlateIndex) Indices[i];
}
self->renderData.VertexData.SetNumUninitialized(Vertices.Num());
FSlateVertex *vertexData = (FSlateVertex *) renderData.VertexData.GetData();
FVector2D offset = AllottedGeometry.GetAbsolutePositionAtCoordinates(FVector2D(0.0f, 0.0f));
FColor white = FColor(0xffffffff);
const FSlateRenderTransform &Transform = AllottedGeometry.GetAccumulatedRenderTransform();
for (size_t i = 0; i < (size_t) Vertices.Num(); i++) {
setVertex(&vertexData[i], 0, 0, Uvs[i].X, Uvs[i].Y, Colors[i], Transform.TransformPoint(FVector2D(Vertices[i])));
}
brush = &widget->Brush;
if (Material) {
renderData.Brush = MakeShareable(new SpineSlateMaterialBrush(*Material, FVector2D(64, 64)));
renderData.RenderingResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(*renderData.Brush);
}
if (renderData.RenderingResourceHandle.IsValid()) {
FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, renderData.RenderingResourceHandle, renderData.VertexData, renderData.IndexData, nullptr, 0, 0);
}
Vertices.SetNum(0);
Indices.SetNum(0);
Uvs.SetNum(0);
Colors.SetNum(0);
Colors2.SetNum(0);
Idx++;
}
FVector2D SSpineWidget::ComputeDesiredSize(float X) const {
if (widget && widget->skeleton && widget->Atlas) {
return FVector2D(boundsSize.X, boundsSize.Y);
} else {
return FVector2D(256, 256);
}
}
void SSpineWidget::UpdateMesh(int32 LayerId, FSlateWindowElementList &OutDrawElements, const FGeometry &AllottedGeometry, Skeleton *Skeleton) {
TArray<FVector> vertices;
TArray<int32> indices;
TArray<FVector2D> uvs;
TArray<FColor> colors;
TArray<FVector> darkColors;
int idx = 0;
int meshSection = 0;
UMaterialInstanceDynamic *lastMaterial = nullptr;
SkeletonClipping &clipper = widget->clipper;
Vector<float> &worldVertices = widget->worldVertices;
float depthOffset = 0;
unsigned short quadIndices[] = {0, 1, 2, 0, 2, 3};
for (int i = 0; i < (int) Skeleton->getSlots().size(); ++i) {
Vector<float> *attachmentVertices = &worldVertices;
unsigned short *attachmentIndices = nullptr;
int numVertices;
int numIndices;
AtlasRegion *attachmentAtlasRegion = nullptr;
Color attachmentColor;
attachmentColor.set(1, 1, 1, 1);
float *attachmentUvs = nullptr;
Slot *slot = Skeleton->getDrawOrder()[i];
if (!slot->getBone().isActive()) {
clipper.clipEnd(*slot);
continue;
}
Attachment *attachment = slot->getAttachment();
if (!attachment) {
clipper.clipEnd(*slot);
continue;
}
if (!attachment->getRTTI().isExactly(RegionAttachment::rtti) && !attachment->getRTTI().isExactly(MeshAttachment::rtti) && !attachment->getRTTI().isExactly(ClippingAttachment::rtti)) {
clipper.clipEnd(*slot);
continue;
}
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
RegionAttachment *regionAttachment = (RegionAttachment *) attachment;
attachmentColor.set(regionAttachment->getColor());
attachmentVertices->setSize(8, 0);
regionAttachment->computeWorldVertices(*slot, *attachmentVertices, 0, 2);
attachmentAtlasRegion = (AtlasRegion *) regionAttachment->getRegion();
attachmentIndices = quadIndices;
attachmentUvs = regionAttachment->getUVs().buffer();
numVertices = 4;
numIndices = 6;
} else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
MeshAttachment *mesh = (MeshAttachment *) attachment;
attachmentColor.set(mesh->getColor());
attachmentVertices->setSize(mesh->getWorldVerticesLength(), 0);
mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), attachmentVertices->buffer(), 0, 2);
attachmentAtlasRegion = (AtlasRegion *) mesh->getRegion();
attachmentIndices = mesh->getTriangles().buffer();
attachmentUvs = mesh->getUVs().buffer();
numVertices = mesh->getWorldVerticesLength() >> 1;
numIndices = mesh->getTriangles().size();
} else /* clipping */ {
ClippingAttachment *clip = (ClippingAttachment *) attachment;
clipper.clipStart(*slot, clip);
continue;
}
// if the user switches the atlas data while not having switched
// to the correct skeleton data yet, we won't find any regions.
// ignore regions for which we can't find a material
UMaterialInstanceDynamic *material = nullptr;
switch (slot->getData().getBlendMode()) {
case BlendMode_Normal:
if (!widget->pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) {
clipper.clipEnd(*slot);
continue;
}
material = widget->pageToNormalBlendMaterial[attachmentAtlasRegion->page];
break;
case BlendMode_Additive:
if (!widget->pageToAdditiveBlendMaterial.Contains(attachmentAtlasRegion->page)) {
clipper.clipEnd(*slot);
continue;
}
material = widget->pageToAdditiveBlendMaterial[attachmentAtlasRegion->page];
break;
case BlendMode_Multiply:
if (!widget->pageToMultiplyBlendMaterial.Contains(attachmentAtlasRegion->page)) {
clipper.clipEnd(*slot);
continue;
}
material = widget->pageToMultiplyBlendMaterial[attachmentAtlasRegion->page];
break;
case BlendMode_Screen:
if (!widget->pageToScreenBlendMaterial.Contains(attachmentAtlasRegion->page)) {
clipper.clipEnd(*slot);
continue;
}
material = widget->pageToScreenBlendMaterial[attachmentAtlasRegion->page];
break;
default:
if (!widget->pageToNormalBlendMaterial.Contains(attachmentAtlasRegion->page)) {
clipper.clipEnd(*slot);
continue;
}
material = widget->pageToNormalBlendMaterial[attachmentAtlasRegion->page];
}
if (clipper.isClipping()) {
clipper.clipTriangles(attachmentVertices->buffer(), attachmentIndices, numIndices, attachmentUvs, 2);
attachmentVertices = &clipper.getClippedVertices();
numVertices = clipper.getClippedVertices().size() >> 1;
attachmentIndices = clipper.getClippedTriangles().buffer();
numIndices = clipper.getClippedTriangles().size();
attachmentUvs = clipper.getClippedUVs().buffer();
if (clipper.getClippedTriangles().size() == 0) {
clipper.clipEnd(*slot);
continue;
}
}
if (lastMaterial != material) {
Flush(LayerId, OutDrawElements, AllottedGeometry, meshSection, vertices, indices, uvs, colors, darkColors, lastMaterial);
lastMaterial = material;
idx = 0;
}
uint8 r = static_cast<uint8>(Skeleton->getColor().r * slot->getColor().r * attachmentColor.r * 255);
uint8 g = static_cast<uint8>(Skeleton->getColor().g * slot->getColor().g * attachmentColor.g * 255);
uint8 b = static_cast<uint8>(Skeleton->getColor().b * slot->getColor().b * attachmentColor.b * 255);
uint8 a = static_cast<uint8>(Skeleton->getColor().a * slot->getColor().a * attachmentColor.a * 255);
float dr = slot->hasDarkColor() ? slot->getDarkColor().r : 0.0f;
float dg = slot->hasDarkColor() ? slot->getDarkColor().g : 0.0f;
float db = slot->hasDarkColor() ? slot->getDarkColor().b : 0.0f;
float *verticesPtr = attachmentVertices->buffer();
for (int j = 0; j < numVertices << 1; j += 2) {
colors.Add(FColor(r, g, b, a));
darkColors.Add(FVector(dr, dg, db));
vertices.Add(FVector(verticesPtr[j], -verticesPtr[j + 1], depthOffset));
uvs.Add(FVector2D(attachmentUvs[j], attachmentUvs[j + 1]));
}
for (int j = 0; j < numIndices; j++) {
indices.Add(idx + attachmentIndices[j]);
}
idx += numVertices;
depthOffset += widget->DepthOffset;
clipper.clipEnd(*slot);
}
Flush(LayerId, OutDrawElements, AllottedGeometry, meshSection, vertices, indices, uvs, colors, darkColors, lastMaterial);
clipper.clipEnd();
}

View File

@ -0,0 +1,124 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineAtlasAsset.h"
#include "spine/spine.h"
#include <string.h>
#include <string>
#include "EditorFramework/AssetImportData.h"
#define LOCTEXT_NAMESPACE "Spine"
using namespace spine;
#if WITH_EDITORONLY_DATA
void USpineAtlasAsset::SetAtlasFileName(const FName &AtlasFileName) {
importData->UpdateFilenameOnly(AtlasFileName.ToString());
TArray<FString> files;
importData->ExtractFilenames(files);
if (files.Num() > 0)
atlasFileName = FName(*files[0]);
}
void USpineAtlasAsset::PostInitProperties() {
if (!HasAnyFlags(RF_ClassDefaultObject))
importData = NewObject<UAssetImportData>(this, TEXT("AssetImportData"));
Super::PostInitProperties();
}
void USpineAtlasAsset::Serialize(FArchive &Ar) {
Super::Serialize(Ar);
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION <= 27
if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData)
#else
if (Ar.IsLoading() && Ar.UEVer() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData)
#endif
importData = NewObject<UAssetImportData>(this, TEXT("AssetImportData"));
}
#endif
FName USpineAtlasAsset::GetAtlasFileName() const {
#if WITH_EDITORONLY_DATA
TArray<FString> files;
if (importData)
importData->ExtractFilenames(files);
if (files.Num() > 0)
return FName(*files[0]);
else
return atlasFileName;
#else
return atlasFileName;
#endif
}
void USpineAtlasAsset::SetRawData(const FString &RawData) {
this->rawData = RawData;
if (atlas) {
delete atlas;
atlas = nullptr;
}
}
void USpineAtlasAsset::BeginDestroy() {
if (atlas) {
delete atlas;
atlas = nullptr;
}
Super::BeginDestroy();
}
class UETextureLoader : public TextureLoader {
void load(AtlasPage &page, const String &path) {
page.texture = (void *) (uintptr_t) page.index;
}
void unload(void *texture) {
}
};
UETextureLoader _spineUETextureLoader;
Atlas *USpineAtlasAsset::GetAtlas() {
if (!atlas) {
if (atlas) {
delete atlas;
atlas = nullptr;
}
std::string t = TCHAR_TO_UTF8(*rawData);
atlas = new (__FILE__, __LINE__)
Atlas(t.c_str(), strlen(t.c_str()), "", &_spineUETextureLoader);
}
return this->atlas;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,67 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineBoneDriverComponent.h"
#include "SpineSkeletonComponent.h"
#include "GameFramework/Actor.h"
USpineBoneDriverComponent::USpineBoneDriverComponent() {
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = true;
bAutoActivate = true;
}
void USpineBoneDriverComponent::BeginPlay() {
Super::BeginPlay();
}
void USpineBoneDriverComponent::BeforeUpdateWorldTransform(USpineSkeletonComponent *skeleton) {
if (skeleton == lastBoundComponent && skeleton) {
if (UseComponentTransform) {
skeleton->SetBoneWorldPosition(BoneName, GetComponentLocation());
} else {
AActor *owner = GetOwner();
if (owner) skeleton->SetBoneWorldPosition(BoneName, owner->GetActorLocation());
}
}
}
void USpineBoneDriverComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) {
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (Target) {
USpineSkeletonComponent *skeleton = static_cast<USpineSkeletonComponent *>(Target->GetComponentByClass(USpineSkeletonComponent::StaticClass()));
if (skeleton != lastBoundComponent && skeleton) {
// if (lastBoundComponent) lastBoundComponent->BeforeUpdateWorldTransform.RemoveAll(this);
if (!skeleton->BeforeUpdateWorldTransform.GetAllObjects().Contains(this))
skeleton->BeforeUpdateWorldTransform.AddDynamic(this, &USpineBoneDriverComponent::BeforeUpdateWorldTransform);
lastBoundComponent = skeleton;
}
}
}

View File

@ -0,0 +1,65 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineBoneFollowerComponent.h"
#include "SpineSkeletonComponent.h"
#include "GameFramework/Actor.h"
USpineBoneFollowerComponent::USpineBoneFollowerComponent() {
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = true;
bAutoActivate = true;
}
void USpineBoneFollowerComponent::BeginPlay() {
Super::BeginPlay();
}
void USpineBoneFollowerComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) {
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (Target) {
USpineSkeletonComponent *skeleton = static_cast<USpineSkeletonComponent *>(Target->GetComponentByClass(USpineSkeletonComponent::StaticClass()));
if (skeleton) {
FTransform transform = skeleton->GetBoneWorldTransform(BoneName);
if (UseComponentTransform) {
if (UsePosition) SetWorldLocation(transform.GetLocation());
if (UseRotation) SetWorldRotation(transform.GetRotation());
if (UseScale) SetWorldScale3D(transform.GetScale3D());
} else {
AActor *owner = GetOwner();
if (owner) {
if (UsePosition) owner->SetActorLocation(transform.GetLocation());
if (UseRotation) owner->SetActorRotation(transform.GetRotation());
if (UseScale) owner->SetActorScale3D(transform.GetScale3D());
}
}
}
}
}

View File

@ -0,0 +1,74 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpinePlugin.h"
#include "spine/Extension.h"
DEFINE_LOG_CATEGORY(SpineLog);
class FSpinePlugin : public SpinePlugin {
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
IMPLEMENT_MODULE(FSpinePlugin, SpinePlugin)
void FSpinePlugin::StartupModule() {
}
void FSpinePlugin::ShutdownModule() {}
class Ue4Extension : public spine::DefaultSpineExtension {
public:
Ue4Extension() : spine::DefaultSpineExtension() {}
virtual ~Ue4Extension() {}
virtual void *_alloc(size_t size, const char *file, int line) {
return FMemory::Malloc(size);
}
virtual void *_calloc(size_t size, const char *file, int line) {
void *result = FMemory::Malloc(size);
FMemory::Memset(result, 0, size);
return result;
}
virtual void *_realloc(void *ptr, size_t size, const char *file, int line) {
return FMemory::Realloc(ptr, size);
}
virtual void _free(void *mem, const char *file, int line) {
FMemory::Free(mem);
}
};
spine::SpineExtension *spine::getDefaultExtension() {
return new Ue4Extension();
}

View File

@ -0,0 +1,314 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineSkeletonAnimationComponent.h"
#include "SpineAtlasAsset.h"
#define LOCTEXT_NAMESPACE "Spine"
using namespace spine;
void UTrackEntry::SetTrackEntry(TrackEntry *trackEntry) {
if (entry) entry->setRendererObject(nullptr);
this->entry = trackEntry;
if (entry) entry->setRendererObject((void *) this);
}
void callback(AnimationState *state, spine::EventType type, TrackEntry *entry, Event *event) {
USpineSkeletonAnimationComponent *component = (USpineSkeletonAnimationComponent *) state->getRendererObject();
if (entry->getRendererObject()) {
UTrackEntry *uEntry = (UTrackEntry *) entry->getRendererObject();
if (type == EventType_Start) {
component->AnimationStart.Broadcast(uEntry);
uEntry->AnimationStart.Broadcast(uEntry);
} else if (type == EventType_Interrupt) {
component->AnimationInterrupt.Broadcast(uEntry);
uEntry->AnimationInterrupt.Broadcast(uEntry);
} else if (type == EventType_Event) {
FSpineEvent evt;
evt.SetEvent(event);
component->AnimationEvent.Broadcast(uEntry, evt);
uEntry->AnimationEvent.Broadcast(uEntry, evt);
} else if (type == EventType_Complete) {
component->AnimationComplete.Broadcast(uEntry);
uEntry->AnimationComplete.Broadcast(uEntry);
} else if (type == EventType_End) {
component->AnimationEnd.Broadcast(uEntry);
uEntry->AnimationEnd.Broadcast(uEntry);
} else if (type == EventType_Dispose) {
component->AnimationDispose.Broadcast(uEntry);
uEntry->AnimationDispose.Broadcast(uEntry);
uEntry->SetTrackEntry(nullptr);
component->GCTrackEntry(uEntry);
}
}
}
USpineSkeletonAnimationComponent::USpineSkeletonAnimationComponent() {
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = true;
bAutoActivate = true;
bAutoPlaying = true;
physicsTimeScale = 1;
}
void USpineSkeletonAnimationComponent::BeginPlay() {
Super::BeginPlay();
for (UTrackEntry *entry : trackEntries) {
if (entry && entry->GetTrackEntry()) {
entry->GetTrackEntry()->setRendererObject(nullptr);
}
}
trackEntries.Empty();
}
void UTrackEntry::BeginDestroy() {
Super::BeginDestroy();
}
void USpineSkeletonAnimationComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) {
Super::Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
InternalTick(DeltaTime, true, TickType == LEVELTICK_ViewportsOnly);
}
void USpineSkeletonAnimationComponent::InternalTick(float DeltaTime, bool CallDelegates, bool Preview) {
CheckState();
if (state && bAutoPlaying) {
if (Preview) {
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);
skeleton->update(physicsTimeScale * DeltaTime);
skeleton->updateWorldTransform(Physics_Update);
if (CallDelegates) AfterUpdateWorldTransform.Broadcast(this);
}
}
void USpineSkeletonAnimationComponent::CheckState() {
bool needsUpdate = lastAtlas != Atlas || lastData != SkeletonData;
if (!needsUpdate) {
// Are we doing a re-import? Then check if the underlying spine-cpp data
// has changed.
if (lastAtlas && lastAtlas == Atlas && lastData && lastData == SkeletonData) {
spine::Atlas *atlas = Atlas->GetAtlas();
if (lastSpineAtlas != atlas) {
needsUpdate = true;
}
if (skeleton && skeleton->getData() != SkeletonData->GetSkeletonData(atlas)) {
needsUpdate = true;
}
}
}
if (needsUpdate) {
DisposeState();
if (Atlas && SkeletonData) {
spine::SkeletonData *data = SkeletonData->GetSkeletonData(Atlas->GetAtlas());
if (data) {
skeleton = new (__FILE__, __LINE__) Skeleton(data);
AnimationStateData *stateData = SkeletonData->GetAnimationStateData(Atlas->GetAtlas());
state = new (__FILE__, __LINE__) AnimationState(stateData);
state->setRendererObject((void *) this);
state->setListener(callback);
trackEntries.Empty();
}
}
lastAtlas = Atlas;
lastSpineAtlas = Atlas ? Atlas->GetAtlas() : nullptr;
lastData = SkeletonData;
}
}
void USpineSkeletonAnimationComponent::DisposeState() {
if (state) {
delete state;
state = nullptr;
}
if (skeleton) {
delete skeleton;
skeleton = nullptr;
}
trackEntries.Empty();
}
void USpineSkeletonAnimationComponent::FinishDestroy() {
DisposeState();
Super::FinishDestroy();
}
void USpineSkeletonAnimationComponent::SetAutoPlay(bool bInAutoPlays) {
bAutoPlaying = bInAutoPlays;
}
void USpineSkeletonAnimationComponent::SetPlaybackTime(float InPlaybackTime, bool bCallDelegates) {
CheckState();
if (state && state->getCurrent(0)) {
spine::Animation *CurrentAnimation = state->getCurrent(0)->getAnimation();
const float CurrentTime = state->getCurrent(0)->getTrackTime();
InPlaybackTime = FMath::Clamp(InPlaybackTime, 0.0f, CurrentAnimation->getDuration());
const float DeltaTime = InPlaybackTime - CurrentTime;
state->update(DeltaTime);
state->apply(*skeleton);
//Call delegates and perform the world transform
if (bCallDelegates) {
BeforeUpdateWorldTransform.Broadcast(this);
}
skeleton->updateWorldTransform(Physics_Update);
if (bCallDelegates) {
AfterUpdateWorldTransform.Broadcast(this);
}
}
}
void USpineSkeletonAnimationComponent::SetTimeScale(float timeScale) {
CheckState();
if (state) state->setTimeScale(timeScale);
}
float USpineSkeletonAnimationComponent::GetTimeScale() {
CheckState();
if (state) return state->getTimeScale();
return 1;
}
UTrackEntry *USpineSkeletonAnimationComponent::SetAnimation(int trackIndex, FString animationName, bool loop) {
CheckState();
if (state && skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*animationName))) {
state->disableQueue();
TrackEntry *entry = state->setAnimation(trackIndex, TCHAR_TO_UTF8(*animationName), loop);
state->enableQueue();
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
} else
return NewObject<UTrackEntry>();
}
UTrackEntry *USpineSkeletonAnimationComponent::AddAnimation(int trackIndex, FString animationName, bool loop, float delay) {
CheckState();
if (state && skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*animationName))) {
state->disableQueue();
TrackEntry *entry = state->addAnimation(trackIndex, TCHAR_TO_UTF8(*animationName), loop, delay);
state->enableQueue();
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
} else
return NewObject<UTrackEntry>();
}
UTrackEntry *USpineSkeletonAnimationComponent::SetEmptyAnimation(int trackIndex, float mixDuration) {
CheckState();
if (state) {
TrackEntry *entry = state->setEmptyAnimation(trackIndex, mixDuration);
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
} else
return NewObject<UTrackEntry>();
}
UTrackEntry *USpineSkeletonAnimationComponent::AddEmptyAnimation(int trackIndex, float mixDuration, float delay) {
CheckState();
if (state) {
TrackEntry *entry = state->addEmptyAnimation(trackIndex, mixDuration, delay);
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
} else
return NewObject<UTrackEntry>();
}
UTrackEntry *USpineSkeletonAnimationComponent::GetCurrent(int trackIndex) {
CheckState();
if (state && state->getCurrent(trackIndex)) {
TrackEntry *entry = state->getCurrent(trackIndex);
if (entry->getRendererObject()) {
return (UTrackEntry *) entry->getRendererObject();
} else {
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
}
} else
return NewObject<UTrackEntry>();
}
void USpineSkeletonAnimationComponent::ClearTracks() {
CheckState();
if (state) {
state->clearTracks();
}
}
void USpineSkeletonAnimationComponent::ClearTrack(int trackIndex) {
CheckState();
if (state) {
state->clearTrack(trackIndex);
}
}
void USpineSkeletonAnimationComponent::SetPhysicsTimeScale(float scale) {
physicsTimeScale = scale;
}
float USpineSkeletonAnimationComponent::GetPhysicsTimeScale() {
return physicsTimeScale;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,376 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineSkeletonComponent.h"
#include "SpineSkeletonRendererComponent.h"
#include "SpineAtlasAsset.h"
#include "spine/spine.h"
#define LOCTEXT_NAMESPACE "Spine"
using namespace spine;
USpineSkeletonComponent::USpineSkeletonComponent() {
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = true;
bAutoActivate = true;
}
bool USpineSkeletonComponent::SetSkins(UPARAM(ref) TArray<FString> &SkinNames) {
CheckState();
if (skeleton) {
spine::Skin *newSkin = new spine::Skin("__spine-ue3_custom_skin");
for (auto &skinName : SkinNames) {
spine::Skin *skin = skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName));
if (!skin) {
delete newSkin;
return false;
}
newSkin->addSkin(skin);
}
skeleton->setSkin(newSkin);
if (customSkin != nullptr) {
delete customSkin;
}
customSkin = newSkin;
return true;
} else
return false;
}
bool USpineSkeletonComponent::SetSkin(const FString skinName) {
CheckState();
if (skeleton) {
Skin *skin = skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName));
if (!skin) return false;
skeleton->setSkin(skin);
return true;
} else
return false;
}
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()->findSkin(TCHAR_TO_UTF8(*skinName)) != nullptr;
}
return false;
}
bool USpineSkeletonComponent::SetAttachment(const FString slotName, const FString attachmentName) {
CheckState();
if (skeleton) {
if (attachmentName.IsEmpty()) {
skeleton->setAttachment(TCHAR_TO_UTF8(*slotName), NULL);
return true;
}
if (!skeleton->getAttachment(TCHAR_TO_UTF8(*slotName), TCHAR_TO_UTF8(*attachmentName))) return false;
skeleton->setAttachment(TCHAR_TO_UTF8(*slotName), TCHAR_TO_UTF8(*attachmentName));
return true;
}
return false;
}
FTransform USpineSkeletonComponent::GetBoneWorldTransform(const FString &BoneName) {
CheckState();
if (skeleton) {
Bone *bone = skeleton->findBone(TCHAR_TO_UTF8(*BoneName));
if (!bone) return FTransform();
// Need to fetch the renderer component to get world transform of actor plus
// offset by renderer component and its parent component(s). If no renderer
// component is found, this components owner's transform is used as a fallback
FTransform baseTransform;
AActor *owner = GetOwner();
if (owner) {
USpineSkeletonRendererComponent *rendererComponent = static_cast<USpineSkeletonRendererComponent *>(owner->GetComponentByClass(USpineSkeletonRendererComponent::StaticClass()));
if (rendererComponent) baseTransform = rendererComponent->GetComponentTransform();
else
baseTransform = owner->GetActorTransform();
}
FVector position(bone->getWorldX(), 0, bone->getWorldY());
FMatrix localTransform;
localTransform.SetIdentity();
localTransform.SetAxis(2, FVector(bone->getA(), 0, bone->getC()));
localTransform.SetAxis(0, FVector(bone->getB(), 0, bone->getD()));
localTransform.SetOrigin(FVector(bone->getWorldX(), 0, bone->getWorldY()));
localTransform = localTransform * baseTransform.ToMatrixWithScale();
FTransform result;
result.SetFromMatrix(localTransform);
return result;
}
return FTransform();
}
void USpineSkeletonComponent::SetBoneWorldPosition(const FString &BoneName, const FVector &position) {
CheckState();
if (skeleton) {
Bone *bone = skeleton->findBone(TCHAR_TO_UTF8(*BoneName));
if (!bone) return;
// Need to fetch the renderer component to get world transform of actor plus
// offset by renderer component and its parent component(s). If no renderer
// component is found, this components owner's transform is used as a fallback
FTransform baseTransform;
AActor *owner = GetOwner();
if (owner) {
USpineSkeletonRendererComponent *rendererComponent = static_cast<USpineSkeletonRendererComponent *>(owner->GetComponentByClass(USpineSkeletonRendererComponent::StaticClass()));
if (rendererComponent) baseTransform = rendererComponent->GetComponentTransform();
else
baseTransform = owner->GetActorTransform();
}
baseTransform = baseTransform.Inverse();
FVector localPosition = baseTransform.TransformPosition(position);
float localX = 0, localY = 0;
if (bone->getParent()) {
bone->getParent()->worldToLocal(localPosition.X, localPosition.Z, localX, localY);
} else {
bone->worldToLocal(localPosition.X, localPosition.Z, localX, localY);
}
bone->setX(localX);
bone->setY(localY);
}
}
void USpineSkeletonComponent::UpdateWorldTransform() {
CheckState();
if (skeleton) {
skeleton->updateWorldTransform(Physics_Update);
}
}
void USpineSkeletonComponent::SetToSetupPose() {
CheckState();
if (skeleton) skeleton->setToSetupPose();
}
void USpineSkeletonComponent::SetBonesToSetupPose() {
CheckState();
if (skeleton) skeleton->setBonesToSetupPose();
}
void USpineSkeletonComponent::SetSlotsToSetupPose() {
CheckState();
if (skeleton) skeleton->setSlotsToSetupPose();
}
void USpineSkeletonComponent::SetScaleX(float scaleX) {
CheckState();
if (skeleton) skeleton->setScaleX(scaleX);
}
float USpineSkeletonComponent::GetScaleX() {
CheckState();
if (skeleton) return skeleton->getScaleX();
return 1;
}
void USpineSkeletonComponent::SetScaleY(float scaleY) {
CheckState();
if (skeleton) skeleton->setScaleY(scaleY);
}
float USpineSkeletonComponent::GetScaleY() {
CheckState();
if (skeleton) return skeleton->getScaleY();
return 1;
}
void USpineSkeletonComponent::GetBones(TArray<FString> &Bones) {
CheckState();
if (skeleton) {
for (size_t i = 0, n = skeleton->getBones().size(); i < n; i++) {
Bones.Add(skeleton->getBones()[i]->getData().getName().buffer());
}
}
}
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::SetSlotColor(const FString SlotName, const FColor color) {
CheckState();
if (skeleton) {
Slot *slot = skeleton->findSlot(TCHAR_TO_UTF8(*SlotName));
if (slot) {
slot->getColor().set(color.R / 255.f, color.G / 255.f, color.B / 255.f, color.A / 255.f);
}
}
}
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::PhysicsTranslate(float x, float y) {
CheckState();
if (skeleton) {
skeleton->physicsTranslate(x, y);
}
}
void USpineSkeletonComponent::PhysicsRotate(float x, float y, float degrees) {
CheckState();
if (skeleton) {
skeleton->physicsRotate(x, y, degrees);
}
}
void USpineSkeletonComponent::ResetPhysicsConstraints() {
CheckState();
if (skeleton) {
Vector<PhysicsConstraint *> &constraints = skeleton->getPhysicsConstraints();
for (int i = 0, n = (int) constraints.size(); i < n; i++) {
constraints[i]->reset();
}
}
}
void USpineSkeletonComponent::BeginPlay() {
Super::BeginPlay();
}
void USpineSkeletonComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) {
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
InternalTick(DeltaTime);
}
void USpineSkeletonComponent::InternalTick(float DeltaTime, bool CallDelegates, bool Preview) {
CheckState();
if (skeleton) {
if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this);
skeleton->updateWorldTransform(Physics_Update);
if (CallDelegates) AfterUpdateWorldTransform.Broadcast(this);
}
}
void USpineSkeletonComponent::CheckState() {
bool needsUpdate = lastAtlas != Atlas || lastData != SkeletonData;
if (!needsUpdate) {
// Are we doing a re-import? Then check if the underlying spine-cpp data
// has changed.
if (lastAtlas && lastAtlas == Atlas && lastData && lastData == SkeletonData) {
spine::Atlas *atlas = Atlas->GetAtlas();
if (lastSpineAtlas != atlas) {
needsUpdate = true;
}
if (skeleton && skeleton->getData() != SkeletonData->GetSkeletonData(atlas)) {
needsUpdate = true;
}
}
}
if (needsUpdate) {
DisposeState();
if (Atlas && SkeletonData) {
spine::SkeletonData *data = SkeletonData->GetSkeletonData(Atlas->GetAtlas());
skeleton = new (__FILE__, __LINE__) Skeleton(data);
}
lastAtlas = Atlas;
lastSpineAtlas = Atlas ? Atlas->GetAtlas() : nullptr;
lastData = SkeletonData;
}
}
void USpineSkeletonComponent::DisposeState() {
if (skeleton) {
delete skeleton;
skeleton = nullptr;
}
}
void USpineSkeletonComponent::FinishDestroy() {
DisposeState();
Super::FinishDestroy();
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,404 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineSkeletonDataAsset.h"
#include "EditorFramework/AssetImportData.h"
#include "Runtime/Core/Public/Misc/MessageDialog.h"
#include "SpinePlugin.h"
#include "spine/Version.h"
#include "spine/spine.h"
#include <string>
#define LOCTEXT_NAMESPACE "Spine"
using namespace spine;
FName USpineSkeletonDataAsset::GetSkeletonDataFileName() const {
#if WITH_EDITORONLY_DATA
TArray<FString> files;
if (importData)
importData->ExtractFilenames(files);
if (files.Num() > 0)
return FName(*files[0]);
else
return skeletonDataFileName;
#else
return skeletonDataFileName;
#endif
}
#if WITH_EDITORONLY_DATA
void USpineSkeletonDataAsset::SetSkeletonDataFileName(
const FName &SkeletonDataFileName) {
importData->UpdateFilenameOnly(SkeletonDataFileName.ToString());
TArray<FString> files;
importData->ExtractFilenames(files);
if (files.Num() > 0)
this->skeletonDataFileName = FName(*files[0]);
}
void USpineSkeletonDataAsset::PostInitProperties() {
if (!HasAnyFlags(RF_ClassDefaultObject))
importData = NewObject<UAssetImportData>(this, TEXT("AssetImportData"));
Super::PostInitProperties();
}
#if ((ENGINE_MAJOR_VERSION >= 5) && (ENGINE_MINOR_VERSION >= 4))
void USpineSkeletonDataAsset::GetAssetRegistryTags(FAssetRegistryTagsContext Context) const {
if (importData) {
Context.AddTag(FAssetRegistryTag(SourceFileTagName(), importData->GetSourceData().ToJson(), FAssetRegistryTag::TT_Hidden));
}
Super::GetAssetRegistryTags(Context);
}
#else
void USpineSkeletonDataAsset::GetAssetRegistryTags(
TArray<FAssetRegistryTag> &OutTags) const {
if (importData) {
OutTags.Add(FAssetRegistryTag(SourceFileTagName(),
importData->GetSourceData().ToJson(),
FAssetRegistryTag::TT_Hidden));
}
Super::GetAssetRegistryTags(OutTags);
}
#endif
void USpineSkeletonDataAsset::Serialize(FArchive &Ar) {
Super::Serialize(Ar);
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION <= 27
if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData)
#else
if (Ar.IsLoading() && Ar.UEVer() < VER_UE4_ASSET_IMPORT_DATA_AS_JSON && !importData)
#endif
importData = NewObject<UAssetImportData>(this, TEXT("AssetImportData"));
LoadInfo();
}
#endif
void USpineSkeletonDataAsset::ClearNativeData() {
for (auto &pair : atlasToNativeData) {
if (pair.Value.skeletonData)
delete pair.Value.skeletonData;
if (pair.Value.animationStateData)
delete pair.Value.animationStateData;
}
atlasToNativeData.Empty();
}
void USpineSkeletonDataAsset::BeginDestroy() {
ClearNativeData();
Super::BeginDestroy();
}
class SP_API NullAttachmentLoader : public AttachmentLoader {
public:
virtual RegionAttachment *newRegionAttachment(Skin &skin, const String &name,
const String &path,
Sequence *sequence) {
return new (__FILE__, __LINE__) RegionAttachment(name);
}
virtual MeshAttachment *newMeshAttachment(Skin &skin, const String &name,
const String &path,
Sequence *sequence) {
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::SetRawData(TArray<uint8> &Data) {
this->rawData.Empty();
this->rawData.Append(Data);
ClearNativeData();
LoadInfo();
}
static bool checkVersion(const char *version) {
if (!version)
return false;
char *result = (char *) (strstr(version, SPINE_VERSION_STRING) - version);
return result == 0;
}
static bool checkJson(const char *jsonData) {
Json json(jsonData);
Json *skeleton = Json::getItem(&json, "skeleton");
if (!skeleton)
return false;
const char *version = Json::getString(skeleton, "spine", 0);
if (!version)
return false;
return checkVersion(version);
}
struct BinaryInput {
const unsigned char *cursor;
const unsigned char *end;
};
static unsigned char readByte(BinaryInput *input) { return *input->cursor++; }
static int readVarint(BinaryInput *input, bool optimizePositive) {
unsigned char b = readByte(input);
int value = b & 0x7F;
if (b & 0x80) {
b = readByte(input);
value |= (b & 0x7F) << 7;
if (b & 0x80) {
b = readByte(input);
value |= (b & 0x7F) << 14;
if (b & 0x80) {
b = readByte(input);
value |= (b & 0x7F) << 21;
if (b & 0x80)
value |= (readByte(input) & 0x7F) << 28;
}
}
}
if (!optimizePositive) {
value = (((unsigned int) value >> 1) ^ -(value & 1));
}
return value;
}
static char *readString(BinaryInput *input) {
int length = readVarint(input, true);
char *string;
if (length == 0) {
return NULL;
}
string = SpineExtension::alloc<char>(length, __FILE__, __LINE__);
memcpy(string, input->cursor, length - 1);
input->cursor += length - 1;
string[length - 1] = '\0';
return string;
}
static bool checkBinary(const char *binaryData, int length) {
BinaryInput input;
input.cursor = (const unsigned char *) binaryData;
input.end = (const unsigned char *) binaryData + length;
// Skip hash
input.cursor += 8;
char *version = readString(&input);
bool result = checkVersion(version);
SpineExtension::free(version, __FILE__, __LINE__);
return result;
}
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);
if (checkJson((const char *) rawData.GetData()))
skeletonData = json->readSkeletonData((const char *) rawData.GetData());
if (!skeletonData) {
FMessageDialog::Debugf(FText::FromString(
FString("Couldn't load skeleton data and/or atlas. Please ensure the "
"version of your exported data matches your runtime "
"version.\n\n") +
skeletonDataFileName.GetPlainNameString() + FString("\n\n") +
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);
if (checkBinary((const char *) rawData.GetData(), (int) rawData.Num()))
skeletonData = binary->readSkeletonData(
(const unsigned char *) rawData.GetData(), (int) rawData.Num());
if (!skeletonData) {
FMessageDialog::Debugf(FText::FromString(
FString("Couldn't load skeleton data and/or atlas. Please ensure the "
"version of your exported data matches your runtime "
"version.\n\n") +
skeletonDataFileName.GetPlainNameString() + FString("\n\n") +
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
}
SkeletonData *USpineSkeletonDataAsset::GetSkeletonData(Atlas *Atlas) {
SkeletonData *skeletonData = nullptr;
AnimationStateData *animationStateData = nullptr;
if (atlasToNativeData.Contains(Atlas)) {
skeletonData = atlasToNativeData[Atlas].skeletonData;
animationStateData = atlasToNativeData[Atlas].animationStateData;
}
if (!skeletonData) {
int dataLen = rawData.Num();
if (skeletonDataFileName.GetPlainNameString().Contains(TEXT(".json"))) {
SkeletonJson *json = new (__FILE__, __LINE__) SkeletonJson(Atlas);
if (checkJson((const char *) rawData.GetData()))
skeletonData = json->readSkeletonData((const char *) rawData.GetData());
if (!skeletonData) {
#if WITH_EDITORONLY_DATA
FMessageDialog::Debugf(FText::FromString(
FString("Couldn't load skeleton data and/or atlas. Please ensure "
"the version of your exported data matches your runtime "
"version.\n\n") +
skeletonDataFileName.GetPlainNameString() + FString("\n\n") +
UTF8_TO_TCHAR(json->getError().buffer())));
#endif
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(Atlas);
if (checkBinary((const char *) rawData.GetData(), (int) rawData.Num()))
skeletonData = binary->readSkeletonData(
(const unsigned char *) rawData.GetData(), (int) rawData.Num());
if (!skeletonData) {
#if WITH_EDITORONLY_DATA
FMessageDialog::Debugf(FText::FromString(
FString("Couldn't load skeleton data and/or atlas. Please ensure "
"the version of your exported data matches your runtime "
"version.\n\n") +
skeletonDataFileName.GetPlainNameString() + FString("\n\n") +
UTF8_TO_TCHAR(binary->getError().buffer())));
#endif
UE_LOG(SpineLog, Error,
TEXT("Couldn't load skeleton data and atlas: %s"),
UTF8_TO_TCHAR(binary->getError().buffer()));
}
delete binary;
}
if (skeletonData) {
animationStateData =
new (__FILE__, __LINE__) AnimationStateData(skeletonData);
SetMixes(animationStateData);
atlasToNativeData.Add(Atlas, {skeletonData, animationStateData});
}
}
return skeletonData;
}
void USpineSkeletonDataAsset::SetMixes(AnimationStateData *animationStateData) {
for (auto &data : MixData) {
if (!data.From.IsEmpty() && !data.To.IsEmpty()) {
std::string fromChar = TCHAR_TO_UTF8(*data.From);
std::string toChar = TCHAR_TO_UTF8(*data.To);
animationStateData->setMix(fromChar.c_str(), toChar.c_str(), data.Mix);
}
}
animationStateData->setDefaultMix(DefaultMix);
}
AnimationStateData *
USpineSkeletonDataAsset::GetAnimationStateData(Atlas *atlas) {
if (!atlasToNativeData.Contains(atlas))
return nullptr;
AnimationStateData *data = atlasToNativeData[atlas].animationStateData;
SetMixes(data);
return data;
}
void USpineSkeletonDataAsset::SetMix(const FString &from, const FString &to,
float mix) {
FSpineAnimationStateMixData data;
data.From = from;
data.To = to;
data.Mix = mix;
this->MixData.Add(data);
for (auto &pair : atlasToNativeData) {
SetMixes(pair.Value.animationStateData);
}
}
float USpineSkeletonDataAsset::GetMix(const FString &from, const FString &to) {
for (auto &data : MixData) {
if (data.From.Equals(from) && data.To.Equals(to))
return data.Mix;
}
return 0;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,360 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineSkeletonRendererComponent.h"
#include "SpineAtlasAsset.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "spine/spine.h"
#include "UObject/ConstructorHelpers.h"
#if ENGINE_MAJOR_VERSION >= 5
#include "PhysicsEngine/BodySetup.h"
#endif
#define LOCTEXT_NAMESPACE "Spine"
using namespace spine;
USpineSkeletonRendererComponent::USpineSkeletonRendererComponent(const FObjectInitializer &ObjectInitializer)
: UProceduralMeshComponent(ObjectInitializer) {
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = true;
bAutoActivate = true;
static ConstructorHelpers::FObjectFinder<UMaterialInterface> NormalMaterialRef(TEXT("/SpinePlugin/SpineUnlitNormalMaterial"));
NormalBlendMaterial = NormalMaterialRef.Object;
static ConstructorHelpers::FObjectFinder<UMaterialInterface> AdditiveMaterialRef(TEXT("/SpinePlugin/SpineUnlitAdditiveMaterial"));
AdditiveBlendMaterial = AdditiveMaterialRef.Object;
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MultiplyMaterialRef(TEXT("/SpinePlugin/SpineUnlitMultiplyMaterial"));
MultiplyBlendMaterial = MultiplyMaterialRef.Object;
static ConstructorHelpers::FObjectFinder<UMaterialInterface> ScreenMaterialRef(TEXT("/SpinePlugin/SpineUnlitScreenMaterial"));
ScreenBlendMaterial = ScreenMaterialRef.Object;
TextureParameterName = FName(TEXT("SpriteTexture"));
worldVertices.ensureCapacity(1024 * 2);
SetTickGroup(TG_EndPhysics);
}
void USpineSkeletonRendererComponent::FinishDestroy() {
Super::FinishDestroy();
}
void USpineSkeletonRendererComponent::BeginPlay() {
Super::BeginPlay();
}
void USpineSkeletonRendererComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) {
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
AActor *owner = GetOwner();
if (owner) {
UClass *skeletonClass = USpineSkeletonComponent::StaticClass();
USpineSkeletonComponent *skeletonComponent = Cast<USpineSkeletonComponent>(owner->GetComponentByClass(skeletonClass));
UpdateRenderer(skeletonComponent);
}
}
void USpineSkeletonRendererComponent::UpdateRenderer(USpineSkeletonComponent *component) {
if (component && !component->IsBeingDestroyed() && component->GetSkeleton() && component->Atlas) {
component->GetSkeleton()->getColor().set(Color.R, Color.G, Color.B, Color.A);
if (atlasNormalBlendMaterials.Num() != component->Atlas->atlasPages.Num()) {
atlasNormalBlendMaterials.SetNum(0);
atlasAdditiveBlendMaterials.SetNum(0);
atlasMultiplyBlendMaterials.SetNum(0);
atlasScreenBlendMaterials.SetNum(0);
for (int i = 0; i < component->Atlas->atlasPages.Num(); i++) {
AtlasPage *currPage = component->Atlas->GetAtlas()->getPages()[i];
UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(NormalBlendMaterial, this);
material->SetTextureParameterValue(TextureParameterName, component->Atlas->atlasPages[i]);
atlasNormalBlendMaterials.Add(material);
material = UMaterialInstanceDynamic::Create(AdditiveBlendMaterial, this);
material->SetTextureParameterValue(TextureParameterName, component->Atlas->atlasPages[i]);
atlasAdditiveBlendMaterials.Add(material);
material = UMaterialInstanceDynamic::Create(MultiplyBlendMaterial, this);
material->SetTextureParameterValue(TextureParameterName, component->Atlas->atlasPages[i]);
atlasMultiplyBlendMaterials.Add(material);
material = UMaterialInstanceDynamic::Create(ScreenBlendMaterial, this);
material->SetTextureParameterValue(TextureParameterName, component->Atlas->atlasPages[i]);
atlasScreenBlendMaterials.Add(material);
}
} else {
for (int i = 0; i < component->Atlas->atlasPages.Num(); i++) {
UTexture2D *texture = component->Atlas->atlasPages[i];
UpdateMaterial(texture, atlasNormalBlendMaterials[i], NormalBlendMaterial);
UpdateMaterial(texture, atlasAdditiveBlendMaterials[i], AdditiveBlendMaterial);
UpdateMaterial(texture, atlasMultiplyBlendMaterials[i], MultiplyBlendMaterial);
UpdateMaterial(texture, atlasScreenBlendMaterials[i], ScreenBlendMaterial);
}
}
UpdateMesh(component, component->GetSkeleton());
} else {
ClearAllMeshSections();
}
}
void USpineSkeletonRendererComponent::UpdateMaterial(UTexture2D *Texture, UMaterialInstanceDynamic *&CurrentInstance, UMaterialInterface *ParentMaterial) {
UTexture *oldTexture = nullptr;
if (!CurrentInstance || !CurrentInstance->GetTextureParameterValue(TextureParameterName, oldTexture) ||
oldTexture != Texture || CurrentInstance->Parent != ParentMaterial) {
UMaterialInstanceDynamic *material = UMaterialInstanceDynamic::Create(ParentMaterial, this);
material->SetTextureParameterValue(TextureParameterName, Texture);
CurrentInstance = material;
}
}
void USpineSkeletonRendererComponent::Flush(int &Idx, TArray<FVector> &Vertices, TArray<int32> &Indices, TArray<FVector> &Normals, TArray<FVector2D> &Uvs, TArray<FColor> &Colors, UMaterialInstanceDynamic *Material) {
if (Vertices.Num() == 0) return;
SetMaterial(Idx, Material);
bool bShouldCreateCollision = false;
if (bCreateCollision) {
UWorld *world = GetWorld();
if (world && world->IsGameWorld()) {
bShouldCreateCollision = true;
}
}
GetBodySetup()->bGenerateMirroredCollision = GetComponentScale().X < 0 || GetComponentScale().Y < 0 || GetComponentScale().Z < 0;
CreateMeshSection(Idx, Vertices, Indices, Normals, Uvs, Colors, TArray<FProcMeshTangent>(), bShouldCreateCollision);
Vertices.SetNum(0);
Indices.SetNum(0);
Normals.SetNum(0);
Uvs.SetNum(0);
Colors.SetNum(0);
Idx++;
}
void USpineSkeletonRendererComponent::UpdateMesh(USpineSkeletonComponent *component, Skeleton *Skeleton) {
vertices.Empty();
indices.Empty();
normals.Empty();
uvs.Empty();
colors.Empty();
int idx = 0;
int meshSection = 0;
UMaterialInstanceDynamic *lastMaterial = nullptr;
ClearAllMeshSections();
// Early out if skeleton is invisible
if (Skeleton->getColor().a == 0) return;
float depthOffset = 0;
unsigned short quadIndices[] = {0, 1, 2, 0, 2, 3};
for (size_t i = 0; i < Skeleton->getSlots().size(); ++i) {
Vector<float> *attachmentVertices = &worldVertices;
unsigned short *attachmentIndices = nullptr;
int numVertices;
int numIndices;
AtlasRegion *attachmentAtlasRegion = nullptr;
spine::Color attachmentColor;
attachmentColor.set(1, 1, 1, 1);
float *attachmentUvs = nullptr;
Slot *slot = Skeleton->getDrawOrder()[i];
Attachment *attachment = slot->getAttachment();
if (slot->getColor().a == 0 || !slot->getBone().isActive()) {
clipper.clipEnd(*slot);
continue;
}
if (!attachment) {
clipper.clipEnd(*slot);
continue;
}
if (!attachment->getRTTI().isExactly(RegionAttachment::rtti) && !attachment->getRTTI().isExactly(MeshAttachment::rtti) && !attachment->getRTTI().isExactly(ClippingAttachment::rtti)) {
clipper.clipEnd(*slot);
continue;
}
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
RegionAttachment *regionAttachment = (RegionAttachment *) attachment;
// Early out if region is invisible
if (regionAttachment->getColor().a == 0) {
clipper.clipEnd(*slot);
continue;
}
attachmentColor.set(regionAttachment->getColor());
attachmentVertices->setSize(8, 0);
regionAttachment->computeWorldVertices(*slot, *attachmentVertices, 0, 2);
attachmentAtlasRegion = (AtlasRegion *) regionAttachment->getRegion();
attachmentIndices = quadIndices;
attachmentUvs = regionAttachment->getUVs().buffer();
numVertices = 4;
numIndices = 6;
} else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
MeshAttachment *mesh = (MeshAttachment *) attachment;
// Early out if region is invisible
if (mesh->getColor().a == 0) {
clipper.clipEnd(*slot);
continue;
}
attachmentColor.set(mesh->getColor());
attachmentVertices->setSize(mesh->getWorldVerticesLength(), 0);
mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), attachmentVertices->buffer(), 0, 2);
attachmentAtlasRegion = (AtlasRegion *) mesh->getRegion();
attachmentIndices = mesh->getTriangles().buffer();
attachmentUvs = mesh->getUVs().buffer();
numVertices = mesh->getWorldVerticesLength() >> 1;
numIndices = mesh->getTriangles().size();
} else /* clipping */ {
ClippingAttachment *clip = (ClippingAttachment *) attachment;
clipper.clipStart(*slot, clip);
continue;
}
if (clipper.isClipping()) {
clipper.clipTriangles(attachmentVertices->buffer(), attachmentIndices, numIndices, attachmentUvs, 2);
attachmentVertices = &clipper.getClippedVertices();
numVertices = clipper.getClippedVertices().size() >> 1;
attachmentIndices = clipper.getClippedTriangles().buffer();
numIndices = clipper.getClippedTriangles().size();
attachmentUvs = clipper.getClippedUVs().buffer();
if (clipper.getClippedTriangles().size() == 0) {
clipper.clipEnd(*slot);
continue;
}
}
// if the user switches the atlas data while not having switched
// to the correct skeleton data yet, we won't find any regions.
// ignore regions for which we can't find a material
UMaterialInstanceDynamic *material = nullptr;
int foundPageIndex = (int) (intptr_t) attachmentAtlasRegion->rendererObject;
if (foundPageIndex == -1) {
clipper.clipEnd(*slot);
continue;
}
switch (slot->getData().getBlendMode()) {
case BlendMode_Additive:
if (foundPageIndex >= atlasAdditiveBlendMaterials.Num()) {
clipper.clipEnd(*slot);
continue;
}
material = atlasAdditiveBlendMaterials[foundPageIndex];
break;
case BlendMode_Multiply:
if (foundPageIndex >= atlasMultiplyBlendMaterials.Num()) {
clipper.clipEnd(*slot);
continue;
}
material = atlasMultiplyBlendMaterials[foundPageIndex];
break;
case BlendMode_Screen:
if (foundPageIndex >= atlasScreenBlendMaterials.Num()) {
clipper.clipEnd(*slot);
continue;
}
material = atlasScreenBlendMaterials[foundPageIndex];
break;
case BlendMode_Normal:
default:
if (foundPageIndex >= atlasNormalBlendMaterials.Num()) {
clipper.clipEnd(*slot);
continue;
}
material = atlasNormalBlendMaterials[foundPageIndex];
break;
}
if (lastMaterial != material) {
Flush(meshSection, vertices, indices, normals, uvs, colors, lastMaterial);
lastMaterial = material;
idx = 0;
}
SetMaterial(meshSection, material);
uint8 r = static_cast<uint8>(Skeleton->getColor().r * slot->getColor().r * attachmentColor.r * 255);
uint8 g = static_cast<uint8>(Skeleton->getColor().g * slot->getColor().g * attachmentColor.g * 255);
uint8 b = static_cast<uint8>(Skeleton->getColor().b * slot->getColor().b * attachmentColor.b * 255);
uint8 a = static_cast<uint8>(Skeleton->getColor().a * slot->getColor().a * attachmentColor.a * 255);
float *verticesPtr = attachmentVertices->buffer();
for (int j = 0; j < numVertices << 1; j += 2) {
colors.Add(FColor(r, g, b, a));
vertices.Add(FVector(verticesPtr[j], depthOffset, verticesPtr[j + 1]));
uvs.Add(FVector2D(attachmentUvs[j], attachmentUvs[j + 1]));
}
for (int j = 0; j < numIndices; j++) {
indices.Add(idx + attachmentIndices[j]);
}
int numTriangles = indices.Num() / 3;
for (int j = 0; j < numTriangles; j++) {
const int triangleIndex = j * 3;
if (FVector::CrossProduct(
vertices[indices[triangleIndex + 2]] - vertices[indices[triangleIndex]],
vertices[indices[triangleIndex + 1]] - vertices[indices[triangleIndex]])
.Y < 0.f) {
const int32 targetVertex = indices[triangleIndex];
indices[triangleIndex] = indices[triangleIndex + 2];
indices[triangleIndex + 2] = targetVertex;
}
}
FVector normal = FVector(0, 1, 0);
for (int j = 0; j < numVertices; j++) {
normals.Add(normal);
}
idx += numVertices;
depthOffset += this->DepthOffset;
clipper.clipEnd(*slot);
}
Flush(meshSection, vertices, indices, normals, uvs, colors, lastMaterial);
clipper.clipEnd();
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,564 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SpineWidget.h"
#include "SSpineWidget.h"
#include "SpineSkeletonAnimationComponent.h"
#include "spine/spine.h"
#define LOCTEXT_NAMESPACE "Spine"
using namespace spine;
void callbackWidget(AnimationState *state, spine::EventType type, TrackEntry *entry, Event *event) {
USpineWidget *component = (USpineWidget *) state->getRendererObject();
if (entry->getRendererObject()) {
UTrackEntry *uEntry = (UTrackEntry *) entry->getRendererObject();
if (type == EventType_Start) {
component->AnimationStart.Broadcast(uEntry);
uEntry->AnimationStart.Broadcast(uEntry);
} else if (type == EventType_Interrupt) {
component->AnimationInterrupt.Broadcast(uEntry);
uEntry->AnimationInterrupt.Broadcast(uEntry);
} else if (type == EventType_Event) {
FSpineEvent evt;
evt.SetEvent(event);
component->AnimationEvent.Broadcast(uEntry, evt);
uEntry->AnimationEvent.Broadcast(uEntry, evt);
} else if (type == EventType_Complete) {
component->AnimationComplete.Broadcast(uEntry);
uEntry->AnimationComplete.Broadcast(uEntry);
} else if (type == EventType_End) {
component->AnimationEnd.Broadcast(uEntry);
uEntry->AnimationEnd.Broadcast(uEntry);
} else if (type == EventType_Dispose) {
component->AnimationDispose.Broadcast(uEntry);
uEntry->AnimationDispose.Broadcast(uEntry);
uEntry->SetTrackEntry(nullptr);
component->GCTrackEntry(uEntry);
}
}
}
USpineWidget::USpineWidget(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer) {
static ConstructorHelpers::FObjectFinder<UMaterialInterface> NormalMaterialRef(TEXT("/SpinePlugin/UI_SpineUnlitNormalMaterial"));
NormalBlendMaterial = NormalMaterialRef.Object;
static ConstructorHelpers::FObjectFinder<UMaterialInterface> AdditiveMaterialRef(TEXT("/SpinePlugin/UI_SpineUnlitAdditiveMaterial"));
AdditiveBlendMaterial = AdditiveMaterialRef.Object;
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MultiplyMaterialRef(TEXT("/SpinePlugin/UI_SpineUnlitMultiplyMaterial"));
MultiplyBlendMaterial = MultiplyMaterialRef.Object;
static ConstructorHelpers::FObjectFinder<UMaterialInterface> ScreenMaterialRef(TEXT("/SpinePlugin/UI_SpineUnlitScreenMaterial"));
ScreenBlendMaterial = ScreenMaterialRef.Object;
TextureParameterName = FName(TEXT("SpriteTexture"));
physicsTimeScale = 1.0f;
worldVertices.ensureCapacity(1024 * 2);
bAutoPlaying = true;
}
void USpineWidget::SynchronizeProperties() {
Super::SynchronizeProperties();
if (slateWidget.IsValid()) {
CheckState();
if (skeleton) {
if (!bSkinInitialized) {// blueprint On Initialized may be called beforehand
if (InitialSkin != "") SetSkin(InitialSkin);
#if WITH_EDITOR
if (IsDesignTime()) {
if (InitialSkin == "") SetSkin("default");
bSkinInitialized = false;// allow multiple edits in editor
}
#endif
}
Tick(0, false);
slateWidget->SetData(this);
} else {
slateWidget->SetData(nullptr);
}
for (UTrackEntry *entry : trackEntries) {
if (entry && entry->GetTrackEntry()) {
entry->GetTrackEntry()->setRendererObject(nullptr);
}
}
trackEntries.Empty();
}
}
void USpineWidget::ReleaseSlateResources(bool bReleaseChildren) {
Super::ReleaseSlateResources(bReleaseChildren);
slateWidget.Reset();
}
TSharedRef<SWidget> USpineWidget::RebuildWidget() {
this->slateWidget = SNew(SSpineWidget);
return this->slateWidget.ToSharedRef();
}
#if WITH_EDITOR
const FText USpineWidget::GetPaletteCategory() {
return LOCTEXT("Spine", "Spine");
}
#endif
void USpineWidget::Tick(float DeltaTime, bool CallDelegates) {
CheckState();
if (state && bAutoPlaying) {
state->update(DeltaTime);
state->apply(*skeleton);
if (CallDelegates) BeforeUpdateWorldTransform.Broadcast(this);
skeleton->update(physicsTimeScale * DeltaTime);
skeleton->updateWorldTransform(Physics_Update);
if (CallDelegates) AfterUpdateWorldTransform.Broadcast(this);
}
}
void USpineWidget::CheckState() {
bool needsUpdate = lastAtlas != Atlas || lastData != SkeletonData;
if (!needsUpdate) {
// Are we doing a re-import? Then check if the underlying spine-cpp data
// has changed.
if (lastAtlas && lastAtlas == Atlas && lastData && lastData == SkeletonData) {
spine::Atlas *atlas = Atlas->GetAtlas();
if (lastSpineAtlas != atlas) {
needsUpdate = true;
}
if (skeleton && skeleton->getData() != SkeletonData->GetSkeletonData(atlas)) {
needsUpdate = true;
}
}
}
if (needsUpdate) {
DisposeState();
if (Atlas && SkeletonData) {
spine::SkeletonData *data = SkeletonData->GetSkeletonData(Atlas->GetAtlas());
if (data) {
skeleton = new (__FILE__, __LINE__) Skeleton(data);
AnimationStateData *stateData = SkeletonData->GetAnimationStateData(Atlas->GetAtlas());
state = new (__FILE__, __LINE__) AnimationState(stateData);
state->setRendererObject((void *) this);
state->setListener(callbackWidget);
trackEntries.Empty();
skeleton->setToSetupPose();
skeleton->updateWorldTransform(Physics_Update);
slateWidget->SetData(this);
}
}
lastAtlas = Atlas;
lastSpineAtlas = Atlas ? Atlas->GetAtlas() : nullptr;
lastData = SkeletonData;
}
}
void USpineWidget::DisposeState() {
if (state) {
delete state;
state = nullptr;
}
if (skeleton) {
delete skeleton;
skeleton = nullptr;
}
if (customSkin) {
delete customSkin;
customSkin = nullptr;
}
trackEntries.Empty();
}
void USpineWidget::FinishDestroy() {
DisposeState();
Super::FinishDestroy();
}
bool USpineWidget::SetSkin(const FString skinName) {
CheckState();
if (skeleton) {
spine::Skin *skin = skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName));
if (!skin) return false;
skeleton->setSkin(skin);
bSkinInitialized = true;
return true;
} else
return false;
}
bool USpineWidget::SetSkins(UPARAM(ref) TArray<FString> &SkinNames) {
CheckState();
if (skeleton) {
spine::Skin *newSkin = new spine::Skin("__spine-ue3_custom_skin");
for (auto &skinName : SkinNames) {
spine::Skin *skin = skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName));
if (!skin) {
delete newSkin;
return false;
}
newSkin->addSkin(skin);
}
skeleton->setSkin(newSkin);
bSkinInitialized = true;
if (customSkin != nullptr) {
delete customSkin;
}
customSkin = newSkin;
return true;
} else
return false;
}
void USpineWidget::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 USpineWidget::HasSkin(const FString skinName) {
CheckState();
if (skeleton) {
return skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName)) != nullptr;
}
return false;
}
bool USpineWidget::SetAttachment(const FString slotName, const FString attachmentName) {
CheckState();
if (skeleton) {
if (attachmentName.IsEmpty()) {
skeleton->setAttachment(TCHAR_TO_UTF8(*slotName), NULL);
return true;
}
if (!skeleton->getAttachment(TCHAR_TO_UTF8(*slotName), TCHAR_TO_UTF8(*attachmentName))) return false;
skeleton->setAttachment(TCHAR_TO_UTF8(*slotName), TCHAR_TO_UTF8(*attachmentName));
return true;
}
return false;
}
void USpineWidget::UpdateWorldTransform() {
CheckState();
if (skeleton) {
skeleton->updateWorldTransform(Physics_Update);
}
}
void USpineWidget::SetToSetupPose() {
CheckState();
if (skeleton) skeleton->setToSetupPose();
}
void USpineWidget::SetBonesToSetupPose() {
CheckState();
if (skeleton) skeleton->setBonesToSetupPose();
}
void USpineWidget::SetSlotsToSetupPose() {
CheckState();
if (skeleton) skeleton->setSlotsToSetupPose();
}
void USpineWidget::SetScaleX(float scaleX) {
CheckState();
if (skeleton) skeleton->setScaleX(scaleX);
}
float USpineWidget::GetScaleX() {
CheckState();
if (skeleton) return skeleton->getScaleX();
return 1;
}
void USpineWidget::SetScaleY(float scaleY) {
CheckState();
if (skeleton) skeleton->setScaleY(scaleY);
}
float USpineWidget::GetScaleY() {
CheckState();
if (skeleton) return skeleton->getScaleY();
return 1;
}
void USpineWidget::GetBones(TArray<FString> &Bones) {
CheckState();
if (skeleton) {
for (size_t i = 0, n = skeleton->getBones().size(); i < n; i++) {
Bones.Add(skeleton->getBones()[i]->getData().getName().buffer());
}
}
}
bool USpineWidget::HasBone(const FString BoneName) {
CheckState();
if (skeleton) {
return skeleton->getData()->findBone(TCHAR_TO_UTF8(*BoneName)) != nullptr;
}
return false;
}
FTransform USpineWidget::GetBoneTransform(const FString &BoneName) {
CheckState();
if (skeleton) {
Bone *bone = skeleton->findBone(TCHAR_TO_UTF8(*BoneName));
if (!bone) return FTransform();
FMatrix localTransform;
localTransform.SetIdentity();
localTransform.SetAxis(2, FVector(bone->getA(), 0, bone->getC()));
localTransform.SetAxis(0, FVector(bone->getB(), 0, bone->getD()));
localTransform.SetOrigin(FVector(bone->getWorldX(), 0, bone->getWorldY()));
FTransform result;
result.SetFromMatrix(localTransform);
return result;
}
return FTransform();
}
void USpineWidget::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 USpineWidget::HasSlot(const FString SlotName) {
CheckState();
if (skeleton) {
return skeleton->getData()->findSlot(TCHAR_TO_UTF8(*SlotName)) != nullptr;
}
return false;
}
void USpineWidget::SetSlotColor(const FString SlotName, const FColor SlotColor) {
CheckState();
if (skeleton) {
spine::Slot *slot = skeleton->findSlot(TCHAR_TO_UTF8(*SlotName));
if (slot) {
slot->getColor().set(SlotColor.R / 255.f, SlotColor.G / 255.f, SlotColor.B / 255.f, SlotColor.A / 255.f);
}
}
}
void USpineWidget::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 USpineWidget::HasAnimation(FString AnimationName) {
CheckState();
if (skeleton) {
return skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*AnimationName)) != nullptr;
}
return false;
}
float USpineWidget::GetAnimationDuration(FString AnimationName) {
CheckState();
if (skeleton) {
spine::Animation *animation = skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*AnimationName));
if (animation == nullptr) return 0;
else
return animation->getDuration();
}
return 0;
}
void USpineWidget::SetAutoPlay(bool bInAutoPlays) {
bAutoPlaying = bInAutoPlays;
}
void USpineWidget::SetPlaybackTime(float InPlaybackTime, bool bCallDelegates) {
CheckState();
if (state && state->getCurrent(0)) {
spine::Animation *CurrentAnimation = state->getCurrent(0)->getAnimation();
const float CurrentTime = state->getCurrent(0)->getTrackTime();
InPlaybackTime = FMath::Clamp(InPlaybackTime, 0.0f, CurrentAnimation->getDuration());
const float DeltaTime = InPlaybackTime - CurrentTime;
state->update(DeltaTime);
state->apply(*skeleton);
//Call delegates and perform the world transform
if (bCallDelegates) {
BeforeUpdateWorldTransform.Broadcast(this);
}
skeleton->updateWorldTransform(Physics_Update);
if (bCallDelegates) {
AfterUpdateWorldTransform.Broadcast(this);
}
}
}
void USpineWidget::SetTimeScale(float timeScale) {
CheckState();
if (state) state->setTimeScale(timeScale);
}
float USpineWidget::GetTimeScale() {
CheckState();
if (state) return state->getTimeScale();
return 1;
}
UTrackEntry *USpineWidget::SetAnimation(int trackIndex, FString animationName, bool loop) {
CheckState();
if (state && skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*animationName))) {
state->disableQueue();
TrackEntry *entry = state->setAnimation(trackIndex, TCHAR_TO_UTF8(*animationName), loop);
state->enableQueue();
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
} else
return NewObject<UTrackEntry>();
}
UTrackEntry *USpineWidget::AddAnimation(int trackIndex, FString animationName, bool loop, float delay) {
CheckState();
if (state && skeleton->getData()->findAnimation(TCHAR_TO_UTF8(*animationName))) {
state->disableQueue();
TrackEntry *entry = state->addAnimation(trackIndex, TCHAR_TO_UTF8(*animationName), loop, delay);
state->enableQueue();
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
} else
return NewObject<UTrackEntry>();
}
UTrackEntry *USpineWidget::SetEmptyAnimation(int trackIndex, float mixDuration) {
CheckState();
if (state) {
TrackEntry *entry = state->setEmptyAnimation(trackIndex, mixDuration);
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
} else
return NewObject<UTrackEntry>();
}
UTrackEntry *USpineWidget::AddEmptyAnimation(int trackIndex, float mixDuration, float delay) {
CheckState();
if (state) {
TrackEntry *entry = state->addEmptyAnimation(trackIndex, mixDuration, delay);
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
} else
return NewObject<UTrackEntry>();
}
UTrackEntry *USpineWidget::GetCurrent(int trackIndex) {
CheckState();
if (state && state->getCurrent(trackIndex)) {
TrackEntry *entry = state->getCurrent(trackIndex);
if (entry->getRendererObject()) {
return (UTrackEntry *) entry->getRendererObject();
} else {
UTrackEntry *uEntry = NewObject<UTrackEntry>();
uEntry->SetTrackEntry(entry);
trackEntries.Add(uEntry);
return uEntry;
}
} else
return NewObject<UTrackEntry>();
}
void USpineWidget::ClearTracks() {
CheckState();
if (state) {
state->clearTracks();
}
}
void USpineWidget::ClearTrack(int trackIndex) {
CheckState();
if (state) {
state->clearTrack(trackIndex);
}
}
void USpineWidget::PhysicsTranslate(float x, float y) {
CheckState();
if (skeleton) {
skeleton->physicsTranslate(x, y);
}
}
void USpineWidget::PhysicsRotate(float x, float y, float degrees) {
CheckState();
if (skeleton) {
skeleton->physicsRotate(x, y, degrees);
}
}
void USpineWidget::ResetPhysicsConstraints() {
CheckState();
if (skeleton) {
Vector<PhysicsConstraint *> &constraints = skeleton->getPhysicsConstraints();
for (int i = 0, n = (int) constraints.size(); i < n; i++) {
constraints[i]->reset();
}
}
}
void USpineWidget::SetPhysicsTimeScale(float scale) {
physicsTimeScale = scale;
}
float USpineWidget::GetPhysicsTimeScale() {
return physicsTimeScale;
}

View File

@ -0,0 +1,64 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
#include "Slate/SMeshWidget.h"
#include "SlateCore.h"
#include "SpineAtlasAsset.h"
#include <spine/spine.h>
class USpineWidget;
class SSpineWidget : public SMeshWidget {
public:
SLATE_BEGIN_ARGS(SSpineWidget) : _MeshData(nullptr) {}
SLATE_ARGUMENT(USlateVectorArtData *, MeshData)
SLATE_END_ARGS()
void Construct(const FArguments &Args);
void SetData(USpineWidget *Widget);
FSlateBrush *brush;
protected:
virtual int32 OnPaint(const FPaintArgs &Args, const FGeometry &AllottedGeometry, const FSlateRect &MyCullingRect, FSlateWindowElementList &OutDrawElements, int32 LayerId, const FWidgetStyle &InWidgetStyle, bool bParentEnabled) const override;
void UpdateMesh(int32 LayerId, FSlateWindowElementList &OutDrawElements, const FGeometry &AllottedGeometry, spine::Skeleton *Skeleton);
void Flush(int32 LayerId, FSlateWindowElementList &OutDrawElements, const FGeometry &AllottedGeometry, int &Idx, TArray<FVector> &Vertices, TArray<int32> &Indices, TArray<FVector2D> &Uvs, TArray<FColor> &Colors, TArray<FVector> &Colors2, UMaterialInstanceDynamic *Material);
virtual FVector2D ComputeDesiredSize(float) const override;
USpineWidget *widget;
FRenderData renderData;
FVector boundsMin;
FVector boundsSize;
};

View File

@ -0,0 +1,75 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
// clang-format off
#include "Engine/Texture2D.h"
#include "spine/spine.h"
#include "SpineAtlasAsset.generated.h"
// clang-format on
UCLASS(BlueprintType, ClassGroup = (Spine))
class SPINEPLUGIN_API USpineAtlasAsset : public UPrimaryDataAsset {
GENERATED_BODY()
public:
spine::Atlas *GetAtlas();
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<UTexture2D *> atlasPages;
void SetRawData(const FString &RawData);
FName GetAtlasFileName() const;
virtual void BeginDestroy() override;
protected:
spine::Atlas *atlas = nullptr;
UPROPERTY()
FString rawData;
UPROPERTY()
FName atlasFileName;
#if WITH_EDITORONLY_DATA
public:
void SetAtlasFileName(const FName &AtlasFileName);
protected:
UPROPERTY(VisibleAnywhere, Instanced, Category = ImportSettings)
class UAssetImportData *importData;
virtual void PostInitProperties() override;
virtual void Serialize(FArchive &Ar) override;
#endif
};

View File

@ -0,0 +1,72 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
#include "Components/SceneComponent.h"
#include "SpineBoneDriverComponent.generated.h"
class USpineSkeletonComponent;
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class SPINEPLUGIN_API USpineBoneDriverComponent : public USceneComponent {
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
AActor *Target = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString BoneName;
//Uses just this component when set to true. Updates owning actor otherwise.
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UseComponentTransform = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UsePosition = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UseRotation = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UseScale = true;
USpineBoneDriverComponent();
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
protected:
UFUNCTION()
void BeforeUpdateWorldTransform(USpineSkeletonComponent *skeleton);
USpineSkeletonComponent *lastBoundComponent = nullptr;
};

View File

@ -0,0 +1,66 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
#include "Components/ActorComponent.h"
#include "Components/SceneComponent.h"
#include "SpineBoneFollowerComponent.generated.h"
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class SPINEPLUGIN_API USpineBoneFollowerComponent : public USceneComponent {
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
AActor *Target = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString BoneName;
//Updates just this component when set to true. Updates owning actor otherwise.
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UseComponentTransform = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UsePosition = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UseRotation = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool UseScale = true;
USpineBoneFollowerComponent();
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
};

View File

@ -0,0 +1,46 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
#include "Modules/ModuleManager.h"
DECLARE_LOG_CATEGORY_EXTERN(SpineLog, Log, All);
class SPINEPLUGIN_API SpinePlugin : public IModuleInterface {
public:
static inline SpinePlugin &Get() {
return FModuleManager::LoadModuleChecked<SpinePlugin>("SpinePlugin");
}
static inline bool IsAvailable() {
return FModuleManager::Get().IsModuleLoaded("SpinePlugin");
}
};

View File

@ -0,0 +1,339 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
// clang-format off
#include "Components/ActorComponent.h"
#include "SpineSkeletonComponent.h"
#include "spine/spine.h"
#include "SpineSkeletonAnimationComponent.generated.h"
// clang-format on
USTRUCT(BlueprintType, Category = "Spine")
struct SPINEPLUGIN_API FSpineEvent {
GENERATED_BODY();
public:
FSpineEvent() : IntValue(0), FloatValue(0.0f), Time(0.0f) {}
void SetEvent(spine::Event *event) {
Name = FString(UTF8_TO_TCHAR(event->getData().getName().buffer()));
if (!event->getStringValue().isEmpty()) {
StringValue = FString(UTF8_TO_TCHAR(event->getStringValue().buffer()));
}
this->IntValue = event->getIntValue();
this->FloatValue = event->getFloatValue();
this->Time = event->getTime();
}
UPROPERTY(BlueprintReadonly)
FString Name;
UPROPERTY(BlueprintReadOnly)
FString StringValue;
UPROPERTY(BlueprintReadOnly)
int IntValue;
UPROPERTY(BlueprintReadOnly)
float FloatValue;
UPROPERTY(BlueprintReadOnly)
float Time;
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationStartDelegate, UTrackEntry *, entry);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSpineAnimationEventDelegate, UTrackEntry *, entry, FSpineEvent, evt);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationInterruptDelegate, UTrackEntry *, entry);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationCompleteDelegate, UTrackEntry *, entry);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationEndDelegate, UTrackEntry *, entry);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAnimationDisposeDelegate, UTrackEntry *, entry);
UCLASS(ClassGroup = (Spine), meta = (BlueprintSpawnableComponent), BlueprintType)
class SPINEPLUGIN_API UTrackEntry : public UObject {
GENERATED_BODY()
public:
UTrackEntry() {}
void SetTrackEntry(spine::TrackEntry *trackEntry);
spine::TrackEntry *GetTrackEntry() { return entry; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
int GetTrackIndex() { return entry ? entry->getTrackIndex() : 0; }
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);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetEventThreshold() { return entry ? entry->getEventThreshold() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetEventThreshold(float eventThreshold) {
if (entry) entry->setEventThreshold(eventThreshold);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetAlphaAttachmentThreshold() { return entry ? entry->getAlphaAttachmentThreshold() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetAlphaAttachmentThreshold(float attachmentThreshold) {
if (entry) entry->setAlphaAttachmentThreshold(attachmentThreshold);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetMixDrawOrderThreshold() { return entry ? entry->getMixDrawOrderThreshold() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetMixDrawOrderThreshold(float drawOrderThreshold) {
if (entry) entry->setMixDrawOrderThreshold(drawOrderThreshold);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetMixAttachmentThreshold() { return entry ? entry->getMixAttachmentThreshold() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetMixAttachmentThreshold(float drawOrderThreshold) {
if (entry) entry->setMixAttachmentThreshold(drawOrderThreshold);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetAnimationStart() { return entry ? entry->getAnimationStart() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetAnimationStart(float animationStart) {
if (entry) entry->setAnimationStart(animationStart);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetAnimationEnd() { return entry ? entry->getAnimationEnd() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetAnimationEnd(float animationEnd) {
if (entry) entry->setAnimationEnd(animationEnd);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetAnimationLast() { return entry ? entry->getAnimationLast() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetAnimationLast(float animationLast) {
if (entry) entry->setAnimationLast(animationLast);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetDelay() { return entry ? entry->getDelay() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetDelay(float delay) {
if (entry) entry->setDelay(delay);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetTrackTime() { return entry ? entry->getTrackTime() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetTrackTime(float trackTime) {
if (entry) entry->setTrackTime(trackTime);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetTrackEnd() { return entry ? entry->getTrackEnd() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetTrackEnd(float trackEnd) {
if (entry) entry->setTrackEnd(trackEnd);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetTimeScale() { return entry ? entry->getTimeScale() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetTimeScale(float timeScale) {
if (entry) entry->setTimeScale(timeScale);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetAlpha() { return entry ? entry->getAlpha() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetAlpha(float alpha) {
if (entry) entry->setAlpha(alpha);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetMixTime() { return entry ? entry->getMixTime() : 0; }
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
void SetMixTime(float mixTime) {
if (entry) entry->setMixTime(mixTime);
}
UFUNCTION(BlueprintCallable, Category = "Components|Spine|TrackEntry")
float GetMixDuration() { return entry ? entry->getMixDuration() : 0; }
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")
bool isValidAnimation() { return entry != nullptr; }
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|TrackEntry")
FSpineAnimationStartDelegate AnimationStart;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|TrackEntry")
FSpineAnimationInterruptDelegate AnimationInterrupt;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|TrackEntry")
FSpineAnimationEventDelegate AnimationEvent;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|TrackEntry")
FSpineAnimationCompleteDelegate AnimationComplete;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|TrackEntry")
FSpineAnimationEndDelegate AnimationEnd;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|TrackEntry")
FSpineAnimationDisposeDelegate AnimationDispose;
virtual void BeginDestroy() override;
protected:
spine::TrackEntry *entry = nullptr;
};
class USpineAtlasAsset;
UCLASS(ClassGroup = (Spine), meta = (BlueprintSpawnableComponent))
class SPINEPLUGIN_API USpineSkeletonAnimationComponent : public USpineSkeletonComponent {
GENERATED_BODY()
public:
spine::AnimationState *GetAnimationState() { return state; };
USpineSkeletonAnimationComponent();
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
virtual void FinishDestroy() override;
//Added functions for manual configuration
/* Manages if this skeleton should update automatically or is paused. */
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void SetAutoPlay(bool bInAutoPlays);
/* Directly set the time of the current animation, will clamp to animation range. */
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void SetPlaybackTime(float InPlaybackTime, bool bCallDelegates = true);
// Blueprint functions
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void SetTimeScale(float timeScale);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
float GetTimeScale();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *SetAnimation(int trackIndex, FString animationName, bool loop);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *AddAnimation(int trackIndex, FString animationName, bool loop, float delay);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *SetEmptyAnimation(int trackIndex, float mixDuration);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *AddEmptyAnimation(int trackIndex, float mixDuration, float delay);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *GetCurrent(int trackIndex);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void ClearTracks();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void ClearTrack(int trackIndex);
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationStartDelegate AnimationStart;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationInterruptDelegate AnimationInterrupt;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationEventDelegate AnimationEvent;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationCompleteDelegate AnimationComplete;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationEndDelegate AnimationEnd;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationDisposeDelegate AnimationDispose;
UPROPERTY(EditAnywhere, Category = Spine)
FString PreviewAnimation;
UPROPERTY(EditAnywhere, Category = Spine)
FString PreviewSkin;
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetPhysicsTimeScale(float scale);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
float GetPhysicsTimeScale();
// 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); }
protected:
virtual void CheckState() override;
virtual void InternalTick(float DeltaTime, bool CallDelegates = true, bool Preview = false) override;
virtual void DisposeState() override;
spine::AnimationState *state;
// keep track of track entries so they won't get GCed while
// in transit within a blueprint
UPROPERTY()
TSet<UTrackEntry *> trackEntries;
float physicsTimeScale;
private:
/* If the animation should update automatically. */
UPROPERTY()
bool bAutoPlaying;
FString lastPreviewAnimation;
FString lastPreviewSkin;
};

View File

@ -0,0 +1,163 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
// clang-format off
#include "Components/ActorComponent.h"
#include "SpineSkeletonDataAsset.h"
#include "spine/spine.h"
#include "SpineSkeletonComponent.generated.h"
// clang-format on
class USpineSkeletonComponent;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineBeforeUpdateWorldTransformDelegate, USpineSkeletonComponent *, skeleton);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineAfterUpdateWorldTransformDelegate, USpineSkeletonComponent *, skeleton);
class USpineAtlasAsset;
UCLASS(ClassGroup = (Spine), meta = (BlueprintSpawnableComponent))
class SPINEPLUGIN_API USpineSkeletonComponent : public UActorComponent {
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine)
USpineAtlasAsset *Atlas;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine)
USpineSkeletonDataAsset *SkeletonData;
spine::Skeleton *GetSkeleton() {
CheckState();
return skeleton;
};
UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
void GetSkins(TArray<FString> &Skins);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
bool SetSkins(UPARAM(ref) TArray<FString> &SkinNames);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
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);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetBoneWorldPosition(const FString &BoneName, const FVector &position);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void UpdateWorldTransform();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetToSetupPose();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetBonesToSetupPose();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetSlotsToSetupPose();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetScaleX(float scaleX);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
float GetScaleX();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetScaleY(float scaleY);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
float GetScaleY();
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(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetSlotColor(const FString SlotName, const FColor color);
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);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void PhysicsTranslate(float x, float y);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void PhysicsRotate(float x, float y, float degrees);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void ResetPhysicsConstraints();
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Skeleton")
FSpineBeforeUpdateWorldTransformDelegate BeforeUpdateWorldTransform;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Skeleton")
FSpineAfterUpdateWorldTransformDelegate AfterUpdateWorldTransform;
USpineSkeletonComponent();
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
virtual void FinishDestroy() override;
protected:
virtual void CheckState();
virtual void InternalTick(float DeltaTime, bool CallDelegates = true, bool Preview = false);
virtual void DisposeState();
spine::Skeleton *skeleton;
USpineAtlasAsset *lastAtlas = nullptr;
spine::Atlas *lastSpineAtlas = nullptr;
USpineSkeletonDataAsset *lastData = nullptr;
spine::Skin *customSkin = nullptr;
};

View File

@ -0,0 +1,127 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
// clang-format off
#include "spine/spine.h"
#include "SpineSkeletonDataAsset.generated.h"
// clang-format on
USTRUCT(BlueprintType, Category = "Spine")
struct SPINEPLUGIN_API FSpineAnimationStateMixData {
GENERATED_BODY();
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString From;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString To;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Mix = 0;
};
UCLASS(BlueprintType, ClassGroup = (Spine))
class SPINEPLUGIN_API USpineSkeletonDataAsset : public UObject {
GENERATED_BODY()
public:
spine::SkeletonData *GetSkeletonData(spine::Atlas *Atlas);
spine::AnimationStateData *GetAnimationStateData(spine::Atlas *atlas);
void SetMix(const FString &from, const FString &to, float mix);
float GetMix(const FString &from, const FString &to);
FName GetSkeletonDataFileName() const;
void SetRawData(TArray<uint8> &Data);
virtual void BeginDestroy() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float DefaultMix = 0;
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;
UPROPERTY()
FName skeletonDataFileName;
// These are created at runtime
struct NativeSkeletonData {
spine::SkeletonData *skeletonData;
spine::AnimationStateData *animationStateData;
};
TMap<spine::Atlas *, NativeSkeletonData> atlasToNativeData;
void ClearNativeData();
void SetMixes(spine::AnimationStateData *animationStateData);
#if WITH_EDITORONLY_DATA
public:
void SetSkeletonDataFileName(const FName &skeletonDataFileName);
protected:
UPROPERTY(VisibleAnywhere, Instanced, Category = ImportSettings)
class UAssetImportData *importData = nullptr;
virtual void PostInitProperties() override;
#if ((ENGINE_MAJOR_VERSION >= 5) && (ENGINE_MINOR_VERSION >= 4))
virtual void GetAssetRegistryTags(FAssetRegistryTagsContext Context) const override;
#else
virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag> &OutTags) const override;
#endif
virtual void Serialize(FArchive &Ar) override;
#endif
void LoadInfo();
};

View File

@ -0,0 +1,113 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
#include "Components/ActorComponent.h"
#include "ProceduralMeshComponent.h"
#include "SpineSkeletonAnimationComponent.h"
#include "SpineSkeletonRendererComponent.generated.h"
UCLASS(ClassGroup = (Spine), meta = (BlueprintSpawnableComponent))
class SPINEPLUGIN_API USpineSkeletonRendererComponent : public UProceduralMeshComponent {
GENERATED_BODY()
public:
USpineSkeletonRendererComponent(const FObjectInitializer &ObjectInitializer);
virtual void BeginPlay() 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
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
UMaterialInterface *NormalBlendMaterial;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
UMaterialInterface *AdditiveBlendMaterial;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
UMaterialInterface *MultiplyBlendMaterial;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
UMaterialInterface *ScreenBlendMaterial;
// Need to hold on to the dynamic instances, or the GC will kill us while updating them
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
TArray<UMaterialInstanceDynamic *> atlasNormalBlendMaterials;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
TArray<UMaterialInstanceDynamic *> atlasAdditiveBlendMaterials;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
TArray<UMaterialInstanceDynamic *> atlasMultiplyBlendMaterials;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
TArray<UMaterialInstanceDynamic *> atlasScreenBlendMaterials;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
float DepthOffset = 0.1f;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
FName TextureParameterName;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
FLinearColor Color = FLinearColor(1, 1, 1, 1);
/** Whether to generate collision geometry for the skeleton, or not. */
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
bool bCreateCollision;
virtual void FinishDestroy() override;
protected:
void UpdateMaterial(UTexture2D *Texture, UMaterialInstanceDynamic *&CurrentInstance, UMaterialInterface *ParentMaterial);
void UpdateMesh(USpineSkeletonComponent *component, spine::Skeleton *Skeleton);
void Flush(int &Idx, TArray<FVector> &Vertices, TArray<int32> &Indices, TArray<FVector> &Normals, TArray<FVector2D> &Uvs, TArray<FColor> &Colors, UMaterialInstanceDynamic *Material);
spine::Vector<float> worldVertices;
spine::SkeletonClipping clipper;
UPROPERTY();
TArray<FVector> vertices;
UPROPERTY();
TArray<int32> indices;
UPROPERTY();
TArray<FVector> normals;
UPROPERTY();
TArray<FVector2D> uvs;
UPROPERTY();
TArray<FColor> colors;
};

View File

@ -0,0 +1,287 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#pragma once
// clang-format off
#include "Runtime/UMG/Public/UMG.h"
#include "SpineSkeletonDataAsset.h"
#include "SpineSkeletonAnimationComponent.h"
#include "spine/spine.h"
#include "SpineWidget.generated.h"
// clang-format on
class SSpineWidget;
class USpineWidget;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineWidgetBeforeUpdateWorldTransformDelegate, USpineWidget *, skeleton);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpineWidgetAfterUpdateWorldTransformDelegate, USpineWidget *, skeleton);
UCLASS(ClassGroup = (Spine), meta = (BlueprintSpawnableComponent))
class SPINEPLUGIN_API USpineWidget : public UWidget {
GENERATED_UCLASS_BODY()
public:
virtual void ReleaseSlateResources(bool bReleaseChildren) override;
virtual void SynchronizeProperties() override;
#if WITH_EDITOR
virtual const FText GetPaletteCategory() override;
#endif
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine)
FString InitialSkin;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine)
USpineAtlasAsset *Atlas;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine)
USpineSkeletonDataAsset *SkeletonData;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadOnly)
UMaterialInterface *NormalBlendMaterial;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadOnly)
UMaterialInterface *AdditiveBlendMaterial;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadOnly)
UMaterialInterface *MultiplyBlendMaterial;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadOnly)
UMaterialInterface *ScreenBlendMaterial;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
FName TextureParameterName;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
float DepthOffset = 0.1f;
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadWrite)
FLinearColor Color = FLinearColor(1, 1, 1, 1);
UPROPERTY(Category = Spine, EditAnywhere, BlueprintReadOnly)
FSlateBrush Brush;
UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
void GetSkins(TArray<FString> &Skins);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
bool SetSkin(const FString SkinName);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
bool SetSkins(UPARAM(ref) TArray<FString> &SkinNames);
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")
void UpdateWorldTransform();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetToSetupPose();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetBonesToSetupPose();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetSlotsToSetupPose();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetScaleX(float scaleX);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
float GetScaleX();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetScaleY(float scaleY);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
float GetScaleY();
UFUNCTION(BlueprintPure, Category = "Components|Spine|Skeleton")
void GetBones(TArray<FString> &Bones);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
bool HasBone(const FString BoneName);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
FTransform GetBoneTransform(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(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetSlotColor(const FString SlotName, const FColor SlotColor);
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);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void PhysicsTranslate(float x, float y);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void PhysicsRotate(float x, float y, float degrees);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void ResetPhysicsConstraints();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
void SetPhysicsTimeScale(float scale);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Skeleton")
float GetPhysicsTimeScale();
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Skeleton")
FSpineWidgetBeforeUpdateWorldTransformDelegate BeforeUpdateWorldTransform;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Skeleton")
FSpineWidgetAfterUpdateWorldTransformDelegate AfterUpdateWorldTransform;
/* Manages if this skeleton should update automatically or is paused. */
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void SetAutoPlay(bool bInAutoPlays);
/* Directly set the time of the current animation, will clamp to animation range. */
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void SetPlaybackTime(float InPlaybackTime, bool bCallDelegates = true);
// Blueprint functions
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void SetTimeScale(float timeScale);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
float GetTimeScale();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *SetAnimation(int trackIndex, FString animationName, bool loop);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *AddAnimation(int trackIndex, FString animationName, bool loop, float delay);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *SetEmptyAnimation(int trackIndex, float mixDuration);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *AddEmptyAnimation(int trackIndex, float mixDuration, float delay);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
UTrackEntry *GetCurrent(int trackIndex);
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void ClearTracks();
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void ClearTrack(int trackIndex);
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationStartDelegate AnimationStart;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationInterruptDelegate AnimationInterrupt;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationEventDelegate AnimationEvent;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationCompleteDelegate AnimationComplete;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationEndDelegate AnimationEnd;
UPROPERTY(BlueprintAssignable, Category = "Components|Spine|Animation")
FSpineAnimationDisposeDelegate AnimationDispose;
UFUNCTION(BlueprintCallable, Category = "Components|Spine|Animation")
void Tick(float DeltaTime, bool CallDelegates = true);
virtual void FinishDestroy() override;
// 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); }
protected:
friend class SSpineWidget;
virtual TSharedRef<SWidget> RebuildWidget() override;
virtual void CheckState();
virtual void DisposeState();
TSharedPtr<SSpineWidget> slateWidget;
spine::Skeleton *skeleton;
spine::AnimationState *state;
USpineAtlasAsset *lastAtlas = nullptr;
spine::Atlas *lastSpineAtlas = nullptr;
USpineSkeletonDataAsset *lastData = nullptr;
spine::Skin *customSkin = nullptr;
float physicsTimeScale;
// Need to hold on to the dynamic instances, or the GC will kill us while updating them
UPROPERTY()
TArray<UMaterialInstanceDynamic *> atlasNormalBlendMaterials;
TMap<spine::AtlasPage *, UMaterialInstanceDynamic *> pageToNormalBlendMaterial;
UPROPERTY()
TArray<UMaterialInstanceDynamic *> atlasAdditiveBlendMaterials;
TMap<spine::AtlasPage *, UMaterialInstanceDynamic *> pageToAdditiveBlendMaterial;
UPROPERTY()
TArray<UMaterialInstanceDynamic *> atlasMultiplyBlendMaterials;
TMap<spine::AtlasPage *, UMaterialInstanceDynamic *> pageToMultiplyBlendMaterial;
UPROPERTY()
TArray<UMaterialInstanceDynamic *> atlasScreenBlendMaterials;
TMap<spine::AtlasPage *, UMaterialInstanceDynamic *> pageToScreenBlendMaterial;
spine::Vector<float> worldVertices;
spine::SkeletonClipping clipper;
// keep track of track entries so they won't get GCed while
// in transit within a blueprint
UPROPERTY()
TSet<UTrackEntry *> trackEntries;
private:
/* If the animation should update automatically. */
UPROPERTY()
bool bAutoPlaying;
bool bSkinInitialized = false;
};

View File

@ -0,0 +1,131 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Animation_h
#define Spine_Animation_h
#include <spine/Vector.h>
#include <spine/HashMap.h>
#include <spine/MixBlend.h>
#include <spine/MixDirection.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/Property.h>
namespace spine {
class Timeline;
class Skeleton;
class Event;
class AnimationState;
class SP_API Animation : public SpineObject {
friend class AnimationState;
friend class TrackEntry;
friend class AnimationStateData;
friend class AttachmentTimeline;
friend class RGBATimeline;
friend class RGBTimeline;
friend class AlphaTimeline;
friend class RGBA2Timeline;
friend class RGB2Timeline;
friend class DeformTimeline;
friend class DrawOrderTimeline;
friend class EventTimeline;
friend class IkConstraintTimeline;
friend class PathConstraintMixTimeline;
friend class PathConstraintPositionTimeline;
friend class PathConstraintSpacingTimeline;
friend class RotateTimeline;
friend class ScaleTimeline;
friend class ShearTimeline;
friend class TransformConstraintTimeline;
friend class TranslateTimeline;
friend class TranslateXTimeline;
friend class TranslateYTimeline;
friend class TwoColorTimeline;
public:
Animation(const String &name, Vector<Timeline *> &timelines, float duration);
~Animation();
/// Applies all the animation's timelines to the specified skeleton.
/// See also Timeline::apply(Skeleton&, float, float, Vector, float, MixPose, MixDirection)
void apply(Skeleton &skeleton, float lastTime, float time, bool loop, Vector<Event *> *pEvents, float alpha,
MixBlend blend, MixDirection direction);
const String &getName();
Vector<Timeline *> &getTimelines();
bool hasTimeline(Vector<PropertyId> &ids);
float getDuration();
void setDuration(float inValue);
/// @param target After the first and before the last entry.
static int search(Vector<float> &values, float target);
static int search(Vector<float> &values, float target, int step);
private:
Vector<Timeline *> _timelines;
HashMap<PropertyId, bool> _timelineIds;
float _duration;
String _name;
};
}
#endif /* Spine_Animation_h */

View File

@ -0,0 +1,521 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_AnimationState_h
#define Spine_AnimationState_h
#include <spine/Vector.h>
#include <spine/Pool.h>
#include <spine/Property.h>
#include <spine/MixBlend.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/HasRendererObject.h>
#include "Slot.h"
#ifdef SPINE_USE_STD_FUNCTION
#include <functional>
#endif
namespace spine {
enum EventType {
EventType_Start = 0,
EventType_Interrupt,
EventType_End,
EventType_Complete,
EventType_Dispose,
EventType_Event
};
class AnimationState;
class TrackEntry;
class Animation;
class Event;
class AnimationStateData;
class Skeleton;
class RotateTimeline;
class AttachmentTimeline;
#ifdef SPINE_USE_STD_FUNCTION
typedef std::function<void (AnimationState* state, EventType type, TrackEntry* entry, Event* event)> AnimationStateListener;
#else
typedef void (*AnimationStateListener)(AnimationState *state, EventType type, TrackEntry *entry, Event *event);
#endif
/// Abstract class to inherit from to create a callback object
class SP_API AnimationStateListenerObject {
public:
AnimationStateListenerObject() {};
virtual ~AnimationStateListenerObject() {};
public:
/// The callback function to be called
virtual void callback(AnimationState *state, EventType type, TrackEntry *entry, Event *event) = 0;
};
/// State for the playback of an animation
class SP_API TrackEntry : public SpineObject, public HasRendererObject {
friend class EventQueue;
friend class AnimationState;
public:
TrackEntry();
virtual ~TrackEntry();
/// The index of the track where this entry is either current or queued.
int getTrackIndex();
/// The animation to apply for this track entry.
Animation *getAnimation();
TrackEntry *getPrevious();
/// If true, the animation will repeat. If false, it will not, instead its last frame is applied if played beyond its duration.
bool getLoop();
void setLoop(bool inValue);
/// If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead
/// of being mixed out.
///
/// When mixing between animations that key the same property, if a lower track also keys that property then the value will
/// briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0%
/// while the second animation mixes from 0% to 100%. Setting holdPrevious to true applies the first animation
/// at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which
/// keys the property, only when a higher track also keys the property.
///
/// Snapping will occur if holdPrevious is true and this animation does not key all the same properties as the
/// previous animation.
bool getHoldPrevious();
void setHoldPrevious(bool inValue);
bool getReverse();
void setReverse(bool inValue);
bool getShortestRotation();
void setShortestRotation(bool inValue);
/// Seconds to postpone playing the animation. When a track entry is the current track entry, delay postpones incrementing
/// the track time. When a track entry is queued, delay is the time from the start of the previous animation to when the
/// track entry will become the current track entry.
float getDelay();
void setDelay(float inValue);
/// Current time in seconds this track entry has been the current track entry. The track time determines
/// TrackEntry.AnimationTime. The track time can be set to start the animation at a time other than 0, without affecting looping.
float getTrackTime();
void setTrackTime(float inValue);
/// The track time in seconds when this animation will be removed from the track. Defaults to the animation duration for
/// non-looping animations and to int.MaxValue for looping animations. If the track end time is reached and no
/// other animations are queued for playback, and mixing from any previous animations is complete, properties keyed by the animation,
/// are set to the setup pose and the track is cleared.
///
/// It may be desired to use AnimationState.addEmptyAnimation(int, float, float) to mix the properties back to the
/// setup pose over time, rather than have it happen instantly.
float getTrackEnd();
void setTrackEnd(float inValue);
/// Seconds when this animation starts, both initially and after looping. Defaults to 0.
///
/// When changing the animation start time, it often makes sense to set TrackEntry.AnimationLast to the same value to
/// prevent timeline keys before the start time from triggering.
float getAnimationStart();
void setAnimationStart(float inValue);
/// Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will
/// loop back to TrackEntry.AnimationStart at this time. Defaults to the animation duration.
float getAnimationEnd();
void setAnimationEnd(float inValue);
/// The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this
/// animation is applied, event timelines will fire all events between the animation last time (exclusive) and animation time
/// (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation is applied.
float getAnimationLast();
void setAnimationLast(float inValue);
/// Uses TrackEntry.TrackTime to compute the animation time between TrackEntry.AnimationStart. and
/// TrackEntry.AnimationEnd. When the track time is 0, the animation time is equal to the animation start time.
float getAnimationTime();
/// Multiplier for the delta time when the animation state is updated, causing time for this animation to play slower or
/// faster. Defaults to 1.
float getTimeScale();
void setTimeScale(float inValue);
/// Values less than 1 mix this animation with the last skeleton pose. Defaults to 1, which overwrites the last skeleton pose with
/// this animation.
///
/// Typically track 0 is used to completely pose the skeleton, then alpha can be used on higher tracks. It doesn't make sense
/// to use alpha on track 0 if the skeleton pose is from the last frame render.
float getAlpha();
void setAlpha(float inValue);
///
/// When the mix percentage (mix time / mix duration) is less than the event threshold, event timelines for the animation
/// being mixed out will be applied. Defaults to 0, so event timelines are not applied for an animation being mixed out.
float getEventThreshold();
void setEventThreshold(float inValue);
/// When the mix percentage (mix time / mix duration) is less than the attachment threshold, attachment timelines for the
/// animation being mixed out will be applied. Defaults to 0, so attachment timelines are not applied for an animation being
/// mixed out.
float getMixAttachmentThreshold();
void setMixAttachmentThreshold(float inValue);
/// When getAlpha() is greater than alphaAttachmentThreshold, attachment timelines are applied.
/// Defaults to 0, so attachment timelines are always applied. */
float getAlphaAttachmentThreshold();
void setAlphaAttachmentThreshold(float inValue);
/// When the mix percentage (mix time / mix duration) is less than the draw order threshold, draw order timelines for the
/// animation being mixed out will be applied. Defaults to 0, so draw order timelines are not applied for an animation being
/// mixed out.
float getMixDrawOrderThreshold();
void setMixDrawOrderThreshold(float inValue);
/// The animation queued to start after this animation, or NULL.
TrackEntry *getNext();
/// Returns true if at least one loop has been completed.
bool isComplete();
/// Seconds from 0 to the mix duration when mixing from the previous animation to this animation. May be slightly more than
/// TrackEntry.MixDuration when the mix is complete.
float getMixTime();
void setMixTime(float inValue);
/// Seconds for mixing from the previous animation to this animation. Defaults to the value provided by
/// AnimationStateData based on the animation before this animation (if any).
///
/// The mix duration can be set manually rather than use the value from AnimationStateData.GetMix.
/// In that case, the mixDuration must be set before AnimationState.update(float) is next called.
///
/// When using AnimationState::addAnimation(int, Animation, bool, float) with a delay
/// less than or equal to 0, note the Delay is set using the mix duration from the AnimationStateData
float getMixDuration();
void setMixDuration(float inValue);
void setMixDuration(float mixDuration, float delay);
MixBlend getMixBlend();
void setMixBlend(MixBlend blend);
/// The track entry for the previous animation when mixing from the previous animation to this animation, or NULL if no
/// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a double linked list with MixingTo.
TrackEntry *getMixingFrom();
/// The track entry for the next animation when mixing from this animation, or NULL if no mixing is currently occuring.
/// When mixing from multiple animations, MixingTo makes up a double linked list with MixingFrom.
TrackEntry *getMixingTo();
/// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
/// long way around when using alpha and starting animations on other tracks.
///
/// Mixing involves finding a rotation between two others, which has two possible solutions: the short way or the long way around.
/// The two rotations likely change over time, so which direction is the short or long way also changes.
/// If the short way was always chosen, bones would flip to the other side when that direction became the long way.
/// TrackEntry chooses the short way the first time it is applied and remembers that direction.
void resetRotationDirections();
float getTrackComplete();
void setListener(AnimationStateListener listener);
void setListener(AnimationStateListenerObject *listener);
/// Returns true if this track entry has been applied at least once.
///
/// See AnimationState::apply(Skeleton).
bool wasApplied();
/// Returns true if there is a getNext() track entry that is ready to become the current track entry during the
/// next AnimationState::update(float)}
bool isNextReady () {
return _next != NULL && _nextTrackLast - _next->_delay >= 0;
}
private:
Animation *_animation;
TrackEntry *_previous;
TrackEntry *_next;
TrackEntry *_mixingFrom;
TrackEntry *_mixingTo;
int _trackIndex;
bool _loop, _holdPrevious, _reverse, _shortestRotation;
float _eventThreshold, _mixAttachmentThreshold, _alphaAttachmentThreshold, _mixDrawOrderThreshold;
float _animationStart, _animationEnd, _animationLast, _nextAnimationLast;
float _delay, _trackTime, _trackLast, _nextTrackLast, _trackEnd, _timeScale;
float _alpha, _mixTime, _mixDuration, _interruptAlpha, _totalAlpha;
MixBlend _mixBlend;
Vector<int> _timelineMode;
Vector<TrackEntry *> _timelineHoldMix;
Vector<float> _timelinesRotation;
AnimationStateListener _listener;
AnimationStateListenerObject *_listenerObject;
void reset();
};
class SP_API EventQueueEntry : public SpineObject {
friend class EventQueue;
public:
EventType _type;
TrackEntry *_entry;
Event *_event;
EventQueueEntry(EventType eventType, TrackEntry *trackEntry, Event *event = NULL);
};
class SP_API EventQueue : public SpineObject {
friend class AnimationState;
private:
Vector<EventQueueEntry> _eventQueueEntries;
AnimationState &_state;
bool _drainDisabled;
static EventQueue *newEventQueue(AnimationState &state);
static EventQueueEntry newEventQueueEntry(EventType eventType, TrackEntry *entry, Event *event = NULL);
EventQueue(AnimationState &state);
~EventQueue();
void start(TrackEntry *entry);
void interrupt(TrackEntry *entry);
void end(TrackEntry *entry);
void dispose(TrackEntry *entry);
void complete(TrackEntry *entry);
void event(TrackEntry *entry, Event *event);
/// Raises all events in the queue and drains the queue.
void drain();
};
class SP_API AnimationState : public SpineObject, public HasRendererObject {
friend class TrackEntry;
friend class EventQueue;
public:
explicit AnimationState(AnimationStateData *data);
~AnimationState();
/// Increments the track entry times, setting queued animations as current if needed
/// @param delta delta time
void update(float delta);
/// Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the
/// animation state can be applied to multiple skeletons to pose them identically.
bool apply(Skeleton &skeleton);
/// Removes all animations from all tracks, leaving skeletons in their previous pose.
/// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose,
/// rather than leaving them in their previous pose.
void clearTracks();
/// Removes all animations from the tracks, leaving skeletons in their previous pose.
/// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose,
/// rather than leaving them in their previous pose.
void clearTrack(size_t trackIndex);
/// Sets an animation by name. setAnimation(int, Animation, bool)
TrackEntry *setAnimation(size_t trackIndex, const String &animationName, bool loop);
/// Sets the current animation for a track, discarding any queued animations.
/// @param loop If true, the animation will repeat.
/// If false, it will not, instead its last frame is applied if played beyond its duration.
/// In either case TrackEntry.TrackEnd determines when the track is cleared.
/// @return
/// A track entry to allow further customization of animation playback. References to the track entry must not be kept
/// after AnimationState.Dispose.
TrackEntry *setAnimation(size_t trackIndex, Animation *animation, bool loop);
/// Queues an animation by name.
/// addAnimation(int, Animation, bool, float)
TrackEntry *addAnimation(size_t trackIndex, const String &animationName, bool loop, float delay);
/// Adds an animation to be played delay seconds after the current or last queued animation
/// for a track. If the track is empty, it is equivalent to calling setAnimation.
/// @param delay
/// Seconds to begin this animation after the start of the previous animation. May be &lt;= 0 to use the animation
/// duration of the previous track minus any mix duration plus the negative delay.
///
/// @return A track entry to allow further customization of animation playback. References to the track entry must not be kept
/// after AnimationState.Dispose
TrackEntry *addAnimation(size_t trackIndex, Animation *animation, bool loop, float delay);
/// Sets an empty animation for a track, discarding any queued animations, and mixes to it over the specified mix duration.
TrackEntry *setEmptyAnimation(size_t trackIndex, float mixDuration);
/// Adds an empty animation to be played after the current or last queued animation for a track, and mixes to it over the
/// specified mix duration.
/// @return
/// A track entry to allow further customization of animation playback. References to the track entry must not be kept after AnimationState.Dispose.
///
/// @param trackIndex Track number.
/// @param mixDuration Mix duration.
/// @param delay Seconds to begin this animation after the start of the previous animation. May be &lt;= 0 to use the animation
/// duration of the previous track minus any mix duration plus the negative delay.
TrackEntry *addEmptyAnimation(size_t trackIndex, float mixDuration, float delay);
/// Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration.
void setEmptyAnimations(float mixDuration);
/// @return The track entry for the animation currently playing on the track, or NULL if no animation is currently playing.
TrackEntry *getCurrent(size_t trackIndex);
AnimationStateData *getData();
/// A list of tracks that have animations, which may contain NULLs.
Vector<TrackEntry *> &getTracks();
float getTimeScale();
void setTimeScale(float inValue);
void setListener(AnimationStateListener listener);
void setListener(AnimationStateListenerObject *listener);
void disableQueue();
void enableQueue();
void setManualTrackEntryDisposal(bool inValue);
bool getManualTrackEntryDisposal();
void disposeTrackEntry(TrackEntry *entry);
private:
static const int Subsequent = 0;
static const int First = 1;
static const int HoldSubsequent = 2;
static const int HoldFirst = 3;
static const int HoldMix = 4;
static const int Setup = 1;
static const int Current = 2;
AnimationStateData *_data;
Pool<TrackEntry> _trackEntryPool;
Vector<TrackEntry *> _tracks;
Vector<Event *> _events;
EventQueue *_queue;
HashMap<PropertyId, bool> _propertyIDs;
bool _animationsChanged;
AnimationStateListener _listener;
AnimationStateListenerObject *_listenerObject;
int _unkeyedState;
float _timeScale;
bool _manualTrackEntryDisposal;
static Animation *getEmptyAnimation();
static void
applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleton &skeleton, float time, float alpha, MixBlend pose,
Vector<float> &timelinesRotation, size_t i, bool firstFrame);
void applyAttachmentTimeline(AttachmentTimeline *attachmentTimeline, Skeleton &skeleton, float animationTime,
MixBlend pose, bool firstFrame);
/// Returns true when all mixing from entries are complete.
bool updateMixingFrom(TrackEntry *to, float delta);
float applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBlend currentPose);
void queueEvents(TrackEntry *entry, float animationTime);
/// Sets the active TrackEntry for a given track number.
void setCurrent(size_t index, TrackEntry *current, bool interrupt);
/// Removes the next entry and all entries after it for the specified entry. */
void clearNext(TrackEntry *entry);
TrackEntry *expandToIndex(size_t index);
/// Object-pooling version of new TrackEntry. Obtain an unused TrackEntry from the pool and clear/initialize its values.
/// @param last May be NULL.
TrackEntry *newTrackEntry(size_t trackIndex, Animation *animation, bool loop, TrackEntry *last);
void animationsChanged();
void computeHold(TrackEntry *entry);
void setAttachment(Skeleton &skeleton, spine::Slot &slot, const String &attachmentName, bool attachments);
};
}
#endif /* Spine_AnimationState_h */

View File

@ -0,0 +1,90 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_AnimationStateData_h
#define Spine_AnimationStateData_h
#include <spine/HashMap.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <assert.h>
namespace spine {
class SkeletonData;
class Animation;
/// Stores mix (crossfade) durations to be applied when AnimationState animations are changed.
class SP_API AnimationStateData : public SpineObject {
friend class AnimationState;
public:
explicit AnimationStateData(SkeletonData *skeletonData);
/// The SkeletonData to look up animations when they are specified by name.
SkeletonData *getSkeletonData();
/// The mix duration to use when no mix duration has been specifically defined between two animations.
float getDefaultMix();
void setDefaultMix(float inValue);
/// Sets a mix duration by animation names.
void setMix(const String &fromName, const String &toName, float duration);
/// Sets a mix duration when changing from the specified animation to the other.
/// See TrackEntry.MixDuration.
void setMix(Animation *from, Animation *to, float duration);
/// The mix duration to use when changing from the specified animation to the other,
/// or the DefaultMix if no mix duration has been set.
float getMix(Animation *from, Animation *to);
/// Removes all mixes and sets the default mix to 0.
void clear();
private:
class AnimationPair : public SpineObject {
public:
Animation *_a1;
Animation *_a2;
explicit AnimationPair(Animation *a1 = NULL, Animation *a2 = NULL);
bool operator==(const AnimationPair &other) const;
};
SkeletonData *_skeletonData;
float _defaultMix;
HashMap<AnimationPair, float> _animationToMixTime;
};
}
#endif /* Spine_AnimationStateData_h */

View File

@ -0,0 +1,139 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Atlas_h
#define Spine_Atlas_h
#include <spine/Vector.h>
#include <spine/Extension.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/HasRendererObject.h>
#include "TextureRegion.h"
namespace spine {
enum Format {
Format_Alpha,
Format_Intensity,
Format_LuminanceAlpha,
Format_RGB565,
Format_RGBA4444,
Format_RGB888,
Format_RGBA8888
};
// Our TextureFilter collides with UE4's TextureFilter in unity builds. We rename
// TextureFilter to SpineTextureFilter in UE4.
#ifdef SPINE_UE4
#define TEXTURE_FILTER_ENUM SpineTextureFilter
#else
#define TEXTURE_FILTER_ENUM TextureFilter
#endif
enum TEXTURE_FILTER_ENUM {
TextureFilter_Unknown,
TextureFilter_Nearest,
TextureFilter_Linear,
TextureFilter_MipMap,
TextureFilter_MipMapNearestNearest,
TextureFilter_MipMapLinearNearest,
TextureFilter_MipMapNearestLinear,
TextureFilter_MipMapLinearLinear
};
enum TextureWrap {
TextureWrap_MirroredRepeat,
TextureWrap_ClampToEdge,
TextureWrap_Repeat
};
class SP_API AtlasPage : public SpineObject {
public:
String name;
String texturePath;
Format format;
TEXTURE_FILTER_ENUM minFilter;
TEXTURE_FILTER_ENUM magFilter;
TextureWrap uWrap;
TextureWrap vWrap;
int width, height;
bool pma;
int index;
void *texture;
explicit AtlasPage(const String &inName) : name(inName), format(Format_RGBA8888),
minFilter(TextureFilter_Nearest),
magFilter(TextureFilter_Nearest), uWrap(TextureWrap_ClampToEdge),
vWrap(TextureWrap_ClampToEdge), width(0), height(0), pma(false), index(0), texture(NULL) {
}
};
class SP_API AtlasRegion : public TextureRegion {
public:
AtlasPage *page;
String name;
int index;
int x, y;
Vector<int> splits;
Vector<int> pads;
Vector <String> names;
Vector<float> values;
};
class TextureLoader;
class SP_API Atlas : public SpineObject {
public:
Atlas(const String &path, TextureLoader *textureLoader, bool createTexture = true);
Atlas(const char *data, int length, const char *dir, TextureLoader *textureLoader, bool createTexture = true);
~Atlas();
void flipV();
/// Returns the first region found with the specified name. This method uses String comparison to find the region, so the result
/// should be cached rather than calling this method multiple times.
/// @return The region, or NULL.
AtlasRegion *findRegion(const String &name);
Vector<AtlasPage *> &getPages();
Vector<AtlasRegion *> &getRegions();
private:
Vector<AtlasPage *> _pages;
Vector<AtlasRegion *> _regions;
TextureLoader *_textureLoader;
void load(const char *begin, int length, const char *dir, bool createTexture);
};
}
#endif /* Spine_Atlas_h */

View File

@ -0,0 +1,72 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_AtlasAttachmentLoader_h
#define Spine_AtlasAttachmentLoader_h
#include <spine/AttachmentLoader.h>
#include <spine/Vector.h>
#include <spine/SpineString.h>
namespace spine {
class Atlas;
class AtlasRegion;
/// An AttachmentLoader that configures attachments using texture regions from an Atlas.
/// See http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data about Loading Skeleton Data in the Spine Runtimes Guide.
class SP_API AtlasAttachmentLoader : public AttachmentLoader {
public:
RTTI_DECL
explicit AtlasAttachmentLoader(Atlas *atlas);
virtual RegionAttachment *newRegionAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence);
virtual MeshAttachment *newMeshAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence);
virtual BoundingBoxAttachment *newBoundingBoxAttachment(Skin &skin, const String &name);
virtual PathAttachment *newPathAttachment(Skin &skin, const String &name);
virtual PointAttachment *newPointAttachment(Skin &skin, const String &name);
virtual ClippingAttachment *newClippingAttachment(Skin &skin, const String &name);
virtual void configureAttachment(Attachment *attachment);
AtlasRegion *findRegion(const String &name);
private:
Atlas *_atlas;
};
}
#endif /* Spine_AtlasAttachmentLoader_h */

View File

@ -0,0 +1,62 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Attachment_h
#define Spine_Attachment_h
#include <spine/RTTI.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
namespace spine {
class SP_API Attachment : public SpineObject {
RTTI_DECL
public:
explicit Attachment(const String &name);
virtual ~Attachment();
const String &getName() const;
virtual Attachment *copy() = 0;
int getRefCount();
void reference();
void dereference();
private:
const String _name;
int _refCount;
};
}
#endif /* Spine_Attachment_h */

View File

@ -0,0 +1,84 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_AttachmentLoader_h
#define Spine_AttachmentLoader_h
#include <spine/RTTI.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
namespace spine {
class Skin;
class Attachment;
class RegionAttachment;
class MeshAttachment;
class BoundingBoxAttachment;
class PathAttachment;
class PointAttachment;
class ClippingAttachment;
class Sequence;
class SP_API AttachmentLoader : public SpineObject {
public:
RTTI_DECL
AttachmentLoader();
virtual ~AttachmentLoader();
/// @return May be NULL to not load any attachment.
virtual RegionAttachment *newRegionAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence) = 0;
/// @return May be NULL to not load any attachment.
virtual MeshAttachment *newMeshAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence) = 0;
/// @return May be NULL to not load any attachment.
virtual BoundingBoxAttachment *newBoundingBoxAttachment(Skin &skin, const String &name) = 0;
/// @return May be NULL to not load any attachment
virtual PathAttachment *newPathAttachment(Skin &skin, const String &name) = 0;
virtual PointAttachment *newPointAttachment(Skin &skin, const String &name) = 0;
virtual ClippingAttachment *newClippingAttachment(Skin &skin, const String &name) = 0;
virtual void configureAttachment(Attachment *attachment) = 0;
};
}
#endif /* Spine_AttachmentLoader_h */

View File

@ -0,0 +1,82 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_AttachmentTimeline_h
#define Spine_AttachmentTimeline_h
#include <spine/Timeline.h>
#include <spine/SpineObject.h>
#include <spine/Vector.h>
#include <spine/MixBlend.h>
#include <spine/MixDirection.h>
#include <spine/SpineString.h>
namespace spine {
class Skeleton;
class Slot;
class Event;
class SP_API AttachmentTimeline : public Timeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit AttachmentTimeline(size_t frameCount, int slotIndex);
virtual ~AttachmentTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and value of the specified keyframe.
void setFrame(int frame, float time, const String &attachmentName);
Vector<String> &getAttachmentNames();
int getSlotIndex() { return _slotIndex; }
void setSlotIndex(int inValue) { _slotIndex = inValue; }
protected:
int _slotIndex;
Vector<String> _attachmentNames;
void setAttachment(Skeleton &skeleton, Slot &slot, String *attachmentName);
};
}
#endif /* Spine_AttachmentTimeline_h */

View File

@ -0,0 +1,45 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_AttachmentType_h
#define Spine_AttachmentType_h
namespace spine {
enum AttachmentType {
AttachmentType_Region,
AttachmentType_Boundingbox,
AttachmentType_Mesh,
AttachmentType_Linkedmesh,
AttachmentType_Path,
AttachmentType_Point,
AttachmentType_Clipping
};
}
#endif /* Spine_AttachmentType_h */

View File

@ -0,0 +1,42 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_BlendMode_h
#define Spine_BlendMode_h
namespace spine {
enum BlendMode {
BlendMode_Normal = 0,
BlendMode_Additive,
BlendMode_Multiply,
BlendMode_Screen
};
}
#endif /* Spine_BlendMode_h */

View File

@ -0,0 +1,114 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_BlockAllocator_h
#define Spine_BlockAllocator_h
#include <cstdint>
#include <spine/SpineObject.h>
#include <spine/Extension.h>
#include <spine/MathUtil.h>
#include <spine/Vector.h>
namespace spine {
struct Block {
int size;
int allocated;
uint8_t *memory;
int free() {
return size - allocated;
}
bool canFit(int numBytes) {
return free() >= numBytes;
}
uint8_t *allocate(int numBytes) {
uint8_t *ptr = memory + allocated;
allocated += numBytes;
return ptr;
}
};
class BlockAllocator : public SpineObject {
int initialBlockSize;
Vector <Block> blocks;
public:
BlockAllocator(int initialBlockSize) : initialBlockSize(initialBlockSize) {
blocks.add(newBlock(initialBlockSize));
}
~BlockAllocator() {
for (int i = 0, n = (int) blocks.size(); i < n; i++) {
SpineExtension::free(blocks[i].memory, __FILE__, __LINE__);
}
}
template<typename T>
T *allocate(size_t num) {
return (T *) _allocate((int) (sizeof(T) * num));
}
void compress() {
if (blocks.size() == 1) {
blocks[0].allocated = 0;
return;
}
int totalSize = 0;
for (int i = 0, n = (int)blocks.size(); i < n; i++) {
totalSize += blocks[i].size;
SpineExtension::free(blocks[i].memory, __FILE__, __LINE__);
}
blocks.clear();
blocks.add(newBlock(totalSize));
}
private:
void *_allocate(int numBytes) {
// 16-byte align allocations
int alignedNumBytes = numBytes + (numBytes % 16 != 0 ? 16 - (numBytes % 16) : 0);
Block *block = &blocks[blocks.size() - 1];
if (!block->canFit(alignedNumBytes)) {
blocks.add(newBlock(MathUtil::max(initialBlockSize, alignedNumBytes)));
block = &blocks[blocks.size() - 1];
}
return block->allocate(alignedNumBytes);
}
Block newBlock(int numBytes) {
Block block = {MathUtil::max(initialBlockSize, numBytes), 0, nullptr};
block.memory = SpineExtension::alloc<uint8_t>(block.size, __FILE__, __LINE__);
return block;
}
};
}
#endif

View File

@ -0,0 +1,286 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Bone_h
#define Spine_Bone_h
#include <spine/Updatable.h>
#include <spine/SpineObject.h>
#include <spine/Vector.h>
#include <spine/Inherit.h>
namespace spine {
class BoneData;
class Skeleton;
/// Stores a bone's current pose.
///
/// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
/// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
/// constraint or application code modifies the world transform after it was computed from the local transform.
class SP_API Bone : public Updatable {
friend class AnimationState;
friend class RotateTimeline;
friend class IkConstraint;
friend class TransformConstraint;
friend class VertexAttachment;
friend class PathConstraint;
friend class PhysicsConstraint;
friend class Skeleton;
friend class RegionAttachment;
friend class PointAttachment;
friend class AttachmentTimeline;
friend class RGBATimeline;
friend class RGBTimeline;
friend class AlphaTimeline;
friend class RGBA2Timeline;
friend class RGB2Timeline;
friend class ScaleTimeline;
friend class ScaleXTimeline;
friend class ScaleYTimeline;
friend class ShearTimeline;
friend class ShearXTimeline;
friend class ShearYTimeline;
friend class TranslateTimeline;
friend class TranslateXTimeline;
friend class TranslateYTimeline;
friend class InheritTimeline;
RTTI_DECL
public:
static void setYDown(bool inValue);
static bool isYDown();
/// @param parent May be NULL.
Bone(BoneData &data, Skeleton &skeleton, Bone *parent = NULL);
/// Same as updateWorldTransform. This method exists for Bone to implement Spine::Updatable.
virtual void update(Physics physics);
/// Computes the world transform using the parent bone and this bone's local transform.
void updateWorldTransform();
/// Computes the world transform using the parent bone and the specified local transform.
void
updateWorldTransform(float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY);
/// Computes the individual applied transform values from the world transform. This can be useful to perform processing using
/// the applied transform after the world transform has been modified directly (eg, by a constraint)..
///
/// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation.
void updateAppliedTransform();
void setToSetupPose();
void worldToLocal(float worldX, float worldY, float &outLocalX, float &outLocalY);
void worldToParent(float worldX, float worldY, float &outParentX, float &outParentY);
void localToWorld(float localX, float localY, float &outWorldX, float &outWorldY);
void parentToWorld(float worldX, float worldY, float &outX, float &outY);
float worldToLocalRotation(float worldRotation);
float localToWorldRotation(float localRotation);
/// Rotates the world transform the specified amount and sets isAppliedValid to false.
/// @param degrees Degrees.
void rotateWorld(float degrees);
float getWorldToLocalRotationX();
float getWorldToLocalRotationY();
BoneData &getData();
Skeleton &getSkeleton();
Bone *getParent();
Vector<Bone *> &getChildren();
/// The local X translation.
float getX();
void setX(float inValue);
/// The local Y translation.
float getY();
void setY(float inValue);
/// The local rotation.
float getRotation();
void setRotation(float inValue);
/// The local scaleX.
float getScaleX();
void setScaleX(float inValue);
/// The local scaleY.
float getScaleY();
void setScaleY(float inValue);
/// The local shearX.
float getShearX();
void setShearX(float inValue);
/// The local shearY.
float getShearY();
void setShearY(float inValue);
/// The rotation, as calculated by any constraints.
float getAppliedRotation();
void setAppliedRotation(float inValue);
/// The applied local x translation.
float getAX();
void setAX(float inValue);
/// The applied local y translation.
float getAY();
void setAY(float inValue);
/// The applied local scaleX.
float getAScaleX();
void setAScaleX(float inValue);
/// The applied local scaleY.
float getAScaleY();
void setAScaleY(float inValue);
/// The applied local shearX.
float getAShearX();
void setAShearX(float inValue);
/// The applied local shearY.
float getAShearY();
void setAShearY(float inValue);
float getA();
void setA(float inValue);
float getB();
void setB(float inValue);
float getC();
void setC(float inValue);
float getD();
void setD(float inValue);
float getWorldX();
void setWorldX(float inValue);
float getWorldY();
void setWorldY(float inValue);
float getWorldRotationX();
float getWorldRotationY();
/// Returns the magnitide (always positive) of the world scale X.
float getWorldScaleX();
/// Returns the magnitide (always positive) of the world scale Y.
float getWorldScaleY();
bool isActive();
void setActive(bool inValue);
Inherit getInherit() { return _inherit; }
void setInherit(Inherit inValue) { _inherit = inValue; }
private:
static bool yDown;
BoneData &_data;
Skeleton &_skeleton;
Bone *_parent;
Vector<Bone *> _children;
float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY;
float _ax, _ay, _arotation, _ascaleX, _ascaleY, _ashearX, _ashearY;
float _a, _b, _worldX;
float _c, _d, _worldY;
bool _sorted;
bool _active;
Inherit _inherit;
};
}
#endif /* Spine_Bone_h */

View File

@ -0,0 +1,150 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_BoneData_h
#define Spine_BoneData_h
#include <spine/Inherit.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/Color.h>
namespace spine {
class SP_API BoneData : public SpineObject {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class AnimationState;
friend class RotateTimeline;
friend class ScaleTimeline;
friend class ScaleXTimeline;
friend class ScaleYTimeline;
friend class ShearTimeline;
friend class ShearXTimeline;
friend class ShearYTimeline;
friend class TranslateTimeline;
friend class TranslateXTimeline;
friend class TranslateYTimeline;
public:
BoneData(int index, const String &name, BoneData *parent = NULL);
/// The index of the bone in Skeleton.Bones
int getIndex();
/// The name of the bone, which is unique within the skeleton.
const String &getName();
/// May be NULL.
BoneData *getParent();
float getLength();
void setLength(float inValue);
/// Local X translation.
float getX();
void setX(float inValue);
/// Local Y translation.
float getY();
void setY(float inValue);
/// Local rotation.
float getRotation();
void setRotation(float inValue);
/// Local scaleX.
float getScaleX();
void setScaleX(float inValue);
/// Local scaleY.
float getScaleY();
void setScaleY(float inValue);
/// Local shearX.
float getShearX();
void setShearX(float inValue);
/// Local shearY.
float getShearY();
void setShearY(float inValue);
/// The transform mode for how parent world transforms affect this bone.
Inherit getInherit();
void setInherit(Inherit inValue);
bool isSkinRequired();
void setSkinRequired(bool inValue);
Color &getColor();
const String &getIcon();
void setIcon(const String &icon);
bool isVisible();
void setVisible(bool inValue);
private:
const int _index;
const String _name;
BoneData *_parent;
float _length;
float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY;
Inherit _inherit;
bool _skinRequired;
Color _color;
String _icon;
bool _visible;
};
}
#endif /* Spine_BoneData_h */

View File

@ -0,0 +1,54 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_BoundingBoxAttachment_h
#define Spine_BoundingBoxAttachment_h
#include <spine/VertexAttachment.h>
#include <spine/Color.h>
#include <spine/SpineObject.h>
namespace spine {
/// Attachment that has a polygon for bounds checking.
class SP_API BoundingBoxAttachment : public VertexAttachment {
RTTI_DECL
public:
explicit BoundingBoxAttachment(const String &name);
Color &getColor();
virtual Attachment *copy();
private:
Color _color;
};
}
#endif /* Spine_BoundingBoxAttachment_h */

View File

@ -0,0 +1,65 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_ClippingAttachment_h
#define Spine_ClippingAttachment_h
#include <spine/VertexAttachment.h>
#include <spine/Color.h>
namespace spine {
class SlotData;
class SP_API ClippingAttachment : public VertexAttachment {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class SkeletonClipping;
RTTI_DECL
public:
explicit ClippingAttachment(const String &name);
SlotData *getEndSlot();
void setEndSlot(SlotData *inValue);
Color &getColor();
virtual Attachment *copy();
private:
SlotData *_endSlot;
Color _color;
};
}
#endif /* Spine_ClippingAttachment_h */

View File

@ -0,0 +1,110 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef SPINE_COLOR_H
#define SPINE_COLOR_H
#include <spine/MathUtil.h>
namespace spine {
class SP_API Color : public SpineObject {
public:
Color() : r(0), g(0), b(0), a(0) {
}
Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) {
clamp();
}
inline Color &set(float _r, float _g, float _b, float _a) {
this->r = _r;
this->g = _g;
this->b = _b;
this->a = _a;
clamp();
return *this;
}
inline Color &set(float _r, float _g, float _b) {
this->r = _r;
this->g = _g;
this->b = _b;
clamp();
return *this;
}
inline Color &set(const Color &other) {
r = other.r;
g = other.g;
b = other.b;
a = other.a;
clamp();
return *this;
}
inline Color &add(float _r, float _g, float _b, float _a) {
this->r += _r;
this->g += _g;
this->b += _b;
this->a += _a;
clamp();
return *this;
}
inline Color &add(float _r, float _g, float _b) {
this->r += _r;
this->g += _g;
this->b += _b;
clamp();
return *this;
}
inline Color &add(const Color &other) {
r += other.r;
g += other.g;
b += other.b;
a += other.a;
clamp();
return *this;
}
inline Color &clamp() {
r = MathUtil::clamp(this->r, 0, 1);
g = MathUtil::clamp(this->g, 0, 1);
b = MathUtil::clamp(this->b, 0, 1);
a = MathUtil::clamp(this->a, 0, 1);
return *this;
}
float r, g, b, a;
};
}
#endif //SPINE_COLOR_H

View File

@ -0,0 +1,197 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_ColorTimeline_h
#define Spine_ColorTimeline_h
#include <spine/CurveTimeline.h>
namespace spine {
class SP_API RGBATimeline : public CurveTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit RGBATimeline(size_t frameCount, size_t bezierCount, int slotIndex);
virtual ~RGBATimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and value of the specified keyframe.
void setFrame(int frame, float time, float r, float g, float b, float a);
int getSlotIndex() { return _slotIndex; };
void setSlotIndex(int inValue) { _slotIndex = inValue; }
protected:
int _slotIndex;
static const int ENTRIES = 5;
static const int R = 1;
static const int G = 2;
static const int B = 3;
static const int A = 4;
};
class SP_API RGBTimeline : public CurveTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit RGBTimeline(size_t frameCount, size_t bezierCount, int slotIndex);
virtual ~RGBTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and value of the specified keyframe.
void setFrame(int frame, float time, float r, float g, float b);
int getSlotIndex() { return _slotIndex; };
void setSlotIndex(int inValue) { _slotIndex = inValue; }
protected:
int _slotIndex;
static const int ENTRIES = 4;
static const int R = 1;
static const int G = 2;
static const int B = 3;
};
class SP_API AlphaTimeline : public CurveTimeline1 {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit AlphaTimeline(size_t frameCount, size_t bezierCount, int slotIndex);
virtual ~AlphaTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getSlotIndex() { return _slotIndex; };
void setSlotIndex(int inValue) { _slotIndex = inValue; }
protected:
int _slotIndex;
};
class SP_API RGBA2Timeline : public CurveTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit RGBA2Timeline(size_t frameCount, size_t bezierCount, int slotIndex);
virtual ~RGBA2Timeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and value of the specified keyframe.
void setFrame(int frame, float time, float r, float g, float b, float a, float r2, float g2, float b2);
int getSlotIndex() { return _slotIndex; };
void setSlotIndex(int inValue) { _slotIndex = inValue; }
protected:
int _slotIndex;
static const int ENTRIES = 8;
static const int R = 1;
static const int G = 2;
static const int B = 3;
static const int A = 4;
static const int R2 = 5;
static const int G2 = 6;
static const int B2 = 7;
};
class SP_API RGB2Timeline : public CurveTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit RGB2Timeline(size_t frameCount, size_t bezierCount, int slotIndex);
virtual ~RGB2Timeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and value of the specified keyframe.
void setFrame(int frame, float time, float r, float g, float b, float r2, float g2, float b2);
int getSlotIndex() { return _slotIndex; };
void setSlotIndex(int inValue) { _slotIndex = inValue; }
protected:
int _slotIndex;
static const int ENTRIES = 7;
static const int R = 1;
static const int G = 2;
static const int B = 3;
static const int R2 = 4;
static const int G2 = 5;
static const int B2 = 6;
};
}
#endif /* Spine_ColorTimeline_h */

View File

@ -0,0 +1,69 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Constraint_h
#define Spine_Constraint_h
#include <spine/Updatable.h>
#include <spine/SpineString.h>
namespace spine {
/// The interface for all constraints.
class SP_API ConstraintData : public SpineObject {
friend class SkeletonBinary;
RTTI_DECL
public:
ConstraintData(const String &name);
virtual ~ConstraintData();
/// The IK constraint's name, which is unique within the skeleton.
const String &getName();
/// The ordinal for the order a skeleton's constraints will be applied.
size_t getOrder();
void setOrder(size_t inValue);
/// Whether the constraint is only active for a specific skin.
bool isSkinRequired();
void setSkinRequired(bool inValue);
private:
const String _name;
size_t _order;
bool _skinRequired;
};
}
#endif /* Spine_Constraint_h */

View File

@ -0,0 +1,129 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_ContainerUtil_h
#define Spine_ContainerUtil_h
#include <spine/Extension.h>
#include <spine/Vector.h>
#include <spine/HashMap.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <assert.h>
namespace spine {
class SP_API ContainerUtil : public SpineObject {
public:
/// Finds an item by comparing each item's name.
/// It is more efficient to cache the results of this method than to call it multiple times.
/// @return May be NULL.
template<typename T>
static T *findWithName(Vector<T *> &items, const String &name) {
assert(name.length() > 0);
for (size_t i = 0; i < items.size(); ++i) {
T *item = items[i];
if (item->getName() == name) {
return item;
}
}
return NULL;
}
/// @return -1 if the item was not found.
template<typename T>
static int findIndexWithName(Vector<T *> &items, const String &name) {
assert(name.length() > 0);
for (size_t i = 0, len = items.size(); i < len; ++i) {
T *item = items[i];
if (item->getName() == name) {
return static_cast<int>(i);
}
}
return -1;
}
/// Finds an item by comparing each item's name.
/// It is more efficient to cache the results of this method than to call it multiple times.
/// @return May be NULL.
template<typename T>
static T *findWithDataName(Vector<T *> &items, const String &name) {
assert(name.length() > 0);
for (size_t i = 0; i < items.size(); ++i) {
T *item = items[i];
if (item->getData().getName() == name) {
return item;
}
}
return NULL;
}
/// @return -1 if the item was not found.
template<typename T>
static int findIndexWithDataName(Vector<T *> &items, const String &name) {
assert(name.length() > 0);
for (size_t i = 0, len = items.size(); i < len; ++i) {
T *item = items[i];
if (item->getData().getName() == name) {
return static_cast<int>(i);
}
}
return -1;
}
template<typename T>
static void cleanUpVectorOfPointers(Vector<T *> &items) {
for (int i = (int) items.size() - 1; i >= 0; i--) {
T *item = items[i];
delete item;
items.removeAt(i);
}
}
private:
// ctor, copy ctor, and assignment should be private in a Singleton
ContainerUtil();
ContainerUtil(const ContainerUtil &);
ContainerUtil &operator=(const ContainerUtil &);
};
}
#endif /* Spine_ContainerUtil_h */

View File

@ -0,0 +1,111 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_CurveTimeline_h
#define Spine_CurveTimeline_h
#include <spine/Timeline.h>
#include <spine/Vector.h>
namespace spine {
/// Base class for frames that use an interpolation bezier curve.
class SP_API CurveTimeline : public Timeline {
RTTI_DECL
public:
explicit CurveTimeline(size_t frameCount, size_t frameEntries, size_t bezierCount);
virtual ~CurveTimeline();
void setLinear(size_t frame);
void setStepped(size_t frame);
virtual void
setBezier(size_t bezier, size_t frame, float value, float time1, float value1, float cx1, float cy1, float cx2,
float cy2, float time2, float value2);
float getBezierValue(float time, size_t frame, size_t valueOffset, size_t i);
Vector<float> &getCurves();
protected:
static const int LINEAR = 0;
static const int STEPPED = 1;
static const int BEZIER = 2;
static const int BEZIER_SIZE = 18;
Vector<float> _curves; // type, x, y, ...
};
class SP_API CurveTimeline1 : public CurveTimeline {
RTTI_DECL
public:
explicit CurveTimeline1(size_t frameCount, size_t bezierCount);
virtual ~CurveTimeline1();
void setFrame(size_t frame, float time, float value);
float getCurveValue(float time);
float getRelativeValue(float time, float alpha, MixBlend blend, float current, float setup);
float getAbsoluteValue(float time, float alpha, MixBlend blend, float current, float setup);
float getAbsoluteValue (float time, float alpha, MixBlend blend, float current, float setup, float value);
float getScaleValue (float time, float alpha, MixBlend blend, MixDirection direction, float current, float setup);
protected:
static const int ENTRIES = 2;
static const int VALUE = 1;
};
class SP_API CurveTimeline2 : public CurveTimeline {
RTTI_DECL
public:
explicit CurveTimeline2(size_t frameCount, size_t bezierCount);
virtual ~CurveTimeline2();
void setFrame(size_t frame, float time, float value1, float value2);
float getCurveValue(float time);
protected:
static const int ENTRIES = 3;
static const int VALUE1 = 1;
static const int VALUE2 = 2;
};
}
#endif /* Spine_CurveTimeline_h */

View File

@ -0,0 +1,139 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef SPINE_LOG_H
#define SPINE_LOG_H
#include <spine/Extension.h>
#include <spine/Vector.h>
#include <map>
namespace spine {
class SP_API DebugExtension : public SpineExtension {
struct Allocation {
void *address;
size_t size;
const char *fileName;
int line;
Allocation() : address(NULL), size(0), fileName(NULL), line(0) {
}
Allocation(void *a, size_t s, const char *f, int l) : address(a), size(s), fileName(f), line(l) {
}
};
public:
DebugExtension(SpineExtension *extension) : _extension(extension), _allocations(0), _reallocations(0),
_frees(0) {
}
void reportLeaks() {
for (std::map<void *, Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
printf("\"%s:%i (%zu bytes at %p)\n", it->second.fileName, it->second.line, it->second.size,
it->second.address);
}
printf("allocations: %zu, reallocations: %zu, frees: %zu\n", _allocations, _reallocations, _frees);
if (_allocated.empty()) printf("No leaks detected\n");
}
void clearAllocations() {
_allocated.clear();
_usedMemory = 0;
}
virtual void *_alloc(size_t size, const char *file, int line) {
void *result = _extension->_alloc(size, file, line);
_allocated[result] = Allocation(result, size, file, line);
_allocations++;
_usedMemory += size;
return result;
}
virtual void *_calloc(size_t size, const char *file, int line) {
void *result = _extension->_calloc(size, file, line);
_allocated[result] = Allocation(result, size, file, line);
_allocations++;
_usedMemory += size;
return result;
}
virtual void *_realloc(void *ptr, size_t size, const char *file, int line) {
if (_allocated.count(ptr)) _usedMemory -= _allocated[ptr].size;
_allocated.erase(ptr);
void *result = _extension->_realloc(ptr, size, file, line);
_reallocations++;
_allocated[result] = Allocation(result, size, file, line);
_usedMemory += size;
return result;
}
virtual void _free(void *mem, const char *file, int line) {
if (_allocated.count(mem)) {
_extension->_free(mem, file, line);
_frees++;
_usedMemory -= _allocated[mem].size;
_allocated.erase(mem);
return;
}
printf("%s:%i (address %p): Double free or not allocated through SpineExtension\n", file, line, mem);
_extension->_free(mem, file, line);
}
virtual char *_readFile(const String &path, int *length) {
auto data = _extension->_readFile(path, length);
if (_allocated.count(data) == 0) {
_allocated[data] = Allocation(data, sizeof(char) * (*length), nullptr, 0);
_allocations++;
_usedMemory += sizeof(char) * (*length);
}
return data;
}
size_t getUsedMemory() {
return _usedMemory;
}
private:
SpineExtension *_extension;
std::map<void *, Allocation> _allocated;
size_t _allocations;
size_t _reallocations;
size_t _frees;
size_t _usedMemory;
};
}
#endif //SPINE_LOG_H

View File

@ -0,0 +1,80 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_DeformTimeline_h
#define Spine_DeformTimeline_h
#include <spine/CurveTimeline.h>
namespace spine {
class VertexAttachment;
class SP_API DeformTimeline : public CurveTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit DeformTimeline(size_t frameCount, size_t bezierCount, int slotIndex, VertexAttachment *attachment);
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and value of the specified keyframe.
void setFrame(int frameIndex, float time, Vector<float> &vertices);
Vector <Vector<float>> &getVertices();
VertexAttachment *getAttachment();
void setAttachment(VertexAttachment *inValue);
virtual void
setBezier(size_t bezier, size_t frame, float value, float time1, float value1, float cx1, float cy1, float cx2,
float cy2, float time2, float value2);
float getCurvePercent(float time, int frame);
int getSlotIndex() { return _slotIndex; }
void setSlotIndex(int inValue) { _slotIndex = inValue; }
protected:
int _slotIndex;
Vector <Vector<float>> _vertices;
VertexAttachment *_attachment;
};
}
#endif /* Spine_DeformTimeline_h */

View File

@ -0,0 +1,61 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_DrawOrderTimeline_h
#define Spine_DrawOrderTimeline_h
#include <spine/Timeline.h>
namespace spine {
class SP_API DrawOrderTimeline : public Timeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit DrawOrderTimeline(size_t frameCount);
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and value of the specified keyframe.
/// @param drawOrder May be NULL to use bind pose draw order
void setFrame(size_t frame, float time, Vector<int> &drawOrder);
Vector <Vector<int>> &getDrawOrders();
private:
Vector <Vector<int>> _drawOrders;
};
}
#endif /* Spine_DrawOrderTimeline_h */

View File

@ -0,0 +1,86 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Event_h
#define Spine_Event_h
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
namespace spine {
class EventData;
/// Stores the current pose values for an Event.
class SP_API Event : public SpineObject {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class AnimationState;
public:
Event(float time, const EventData &data);
const EventData &getData();
/// The animation time this event was keyed.
float getTime();
int getIntValue();
void setIntValue(int inValue);
float getFloatValue();
void setFloatValue(float inValue);
const String &getStringValue();
void setStringValue(const String &inValue);
float getVolume();
void setVolume(float inValue);
float getBalance();
void setBalance(float inValue);
private:
const EventData &_data;
const float _time;
int _intValue;
float _floatValue;
String _stringValue;
float _volume;
float _balance;
};
}
#endif /* Spine_Event_h */

View File

@ -0,0 +1,86 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_EventData_h
#define Spine_EventData_h
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
namespace spine {
/// Stores the setup pose values for an Event.
class SP_API EventData : public SpineObject {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class Event;
public:
explicit EventData(const String &name);
/// The name of the event, which is unique within the skeleton.
const String &getName() const;
int getIntValue() const;
void setIntValue(int inValue);
float getFloatValue() const;
void setFloatValue(float inValue);
const String &getStringValue() const;
void setStringValue(const String &inValue);
const String &getAudioPath() const;
void setAudioPath(const String &inValue);
float getVolume() const;
void setVolume(float inValue);
float getBalance() const;
void setBalance(float inValue);
private:
const String _name;
int _intValue;
float _floatValue;
String _stringValue;
String _audioPath;
float _volume;
float _balance;
};
}
#endif /* Spine_EventData_h */

View File

@ -0,0 +1,62 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_EventTimeline_h
#define Spine_EventTimeline_h
#include <spine/Timeline.h>
namespace spine {
class SP_API EventTimeline : public Timeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit EventTimeline(size_t frameCount);
~EventTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and value of the specified keyframe.
void setFrame(size_t frame, Event *event);
Vector<Event *> &getEvents();
private:
Vector<Event *> _events;
};
}
#endif /* Spine_EventTimeline_h */

View File

@ -0,0 +1,125 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Extension_h
#define Spine_Extension_h
#include <stdlib.h>
#include <spine/dll.h>
#define SP_UNUSED(x) (void)(x)
namespace spine {
class String;
class SP_API SpineExtension {
public:
template<typename T>
static T *alloc(size_t num, const char *file, int line) {
return (T *) getInstance()->_alloc(sizeof(T) * num, file, line);
}
template<typename T>
static T *calloc(size_t num, const char *file, int line) {
return (T *) getInstance()->_calloc(sizeof(T) * num, file, line);
}
template<typename T>
static T *realloc(T *ptr, size_t num, const char *file, int line) {
return (T *) getInstance()->_realloc(ptr, sizeof(T) * num, file, line);
}
template<typename T>
static void free(T *ptr, const char *file, int line) {
getInstance()->_free((void *) ptr, file, line);
}
template<typename T>
static void beforeFree(T *ptr) {
getInstance()->_beforeFree((void *) ptr);
}
static char *readFile(const String &path, int *length) {
return getInstance()->_readFile(path, length);
}
static void setInstance(SpineExtension *inSpineExtension);
static SpineExtension *getInstance();
virtual ~SpineExtension();
/// Implement this function to use your own memory allocator
virtual void *_alloc(size_t size, const char *file, int line) = 0;
virtual void *_calloc(size_t size, const char *file, int line) = 0;
virtual void *_realloc(void *ptr, size_t size, const char *file, int line) = 0;
/// If you provide a spineAllocFunc, you should also provide a spineFreeFunc
virtual void _free(void *mem, const char *file, int line) = 0;
virtual char *_readFile(const String &path, int *length) = 0;
virtual void _beforeFree(void *ptr) { SP_UNUSED(ptr); }
protected:
SpineExtension();
private:
static SpineExtension *_instance;
};
class SP_API DefaultSpineExtension : public SpineExtension {
public:
DefaultSpineExtension();
virtual ~DefaultSpineExtension();
protected:
virtual void *_alloc(size_t size, const char *file, int line) override;
virtual void *_calloc(size_t size, const char *file, int line) override;
virtual void *_realloc(void *ptr, size_t size, const char *file, int line) override;
virtual void _free(void *mem, const char *file, int line) override;
virtual char *_readFile(const String &path, int *length) override;
};
// This function is to be implemented by engine specific runtimes to provide
// the default extension for that engine. It is called the first time
// SpineExtension::getInstance() is called, when no instance has been set
// yet.
extern SpineExtension *getDefaultExtension();
}
#endif /* Spine_Extension_h */

View File

@ -0,0 +1,65 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_HasRendererObject_h
#define Spine_HasRendererObject_h
#include <spine/dll.h>
namespace spine {
typedef void (*DisposeRendererObject)(void *rendererObject);
class SP_API HasRendererObject {
public:
explicit HasRendererObject() : _rendererObject(0), _dispose(0) {};
virtual ~HasRendererObject() {
if (_dispose && _rendererObject)
_dispose(_rendererObject);
}
void *getRendererObject() { return _rendererObject; }
void setRendererObject(void *rendererObject, DisposeRendererObject dispose = 0) {
if (_dispose && _rendererObject && _rendererObject != rendererObject)
_dispose(_rendererObject);
_rendererObject = rendererObject;
_dispose = dispose;
}
private:
void *_rendererObject;
DisposeRendererObject _dispose;
};
}
#endif

View File

@ -0,0 +1,200 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_HashMap_h
#define Spine_HashMap_h
#include <spine/Vector.h>
#include <spine/SpineObject.h>
// Required for new with line number and file name in MSVC
#ifdef _MSC_VER
#pragma warning(disable:4291)
#pragma warning(disable:4251)
#endif
namespace spine {
template<typename K, typename V>
class SP_API HashMap : public SpineObject {
private:
class Entry;
public:
class SP_API Pair {
public:
explicit Pair(K &k, V &v) : key(k), value(v) {}
K &key;
V &value;
};
class SP_API Entries {
public:
friend class HashMap;
explicit Entries(Entry *entry) : _entry(NULL), _hasChecked(false) {
_start.next = entry;
_entry = &_start;
}
Pair next() {
assert(_entry);
assert(_hasChecked);
_entry = _entry->next;
Pair pair(_entry->_key, _entry->_value);
_hasChecked = false;
return pair;
}
bool hasNext() {
_hasChecked = true;
return _entry->next;
}
private:
bool _hasChecked;
Entry _start;
Entry *_entry;
};
HashMap() :
_head(NULL),
_size(0) {
}
~HashMap() {
clear();
}
void clear() {
for (Entry *entry = _head; entry != NULL;) {
Entry *next = entry->next;
delete entry;
entry = next;
}
_head = NULL;
_size = 0;
}
size_t size() {
return _size;
}
void put(const K &key, const V &value) {
Entry *entry = find(key);
if (entry) {
entry->_key = key;
entry->_value = value;
} else {
entry = new(__FILE__, __LINE__) Entry();
entry->_key = key;
entry->_value = value;
Entry *oldHead = _head;
if (oldHead) {
_head = entry;
oldHead->prev = entry;
entry->next = oldHead;
} else {
_head = entry;
}
_size++;
}
}
bool addAll(Vector <K> &keys, const V &value) {
size_t oldSize = _size;
for (size_t i = 0; i < keys.size(); i++) {
put(keys[i], value);
}
return _size != oldSize;
}
bool containsKey(const K &key) {
return find(key) != NULL;
}
bool remove(const K &key) {
Entry *entry = find(key);
if (!entry) return false;
Entry *prev = entry->prev;
Entry *next = entry->next;
if (prev) prev->next = next;
else _head = next;
if (next) next->prev = entry->prev;
delete entry;
_size--;
return true;
}
V operator[](const K &key) {
Entry *entry = find(key);
if (entry) return entry->_value;
else {
assert(false);
return 0;
}
}
Entries getEntries() const {
return Entries(_head);
}
private:
Entry *find(const K &key) {
for (Entry *entry = _head; entry != NULL; entry = entry->next) {
if (entry->_key == key)
return entry;
}
return NULL;
}
class SP_API Entry : public SpineObject {
public:
K _key;
V _value;
Entry *next;
Entry *prev;
Entry() : next(NULL), prev(NULL) {}
};
Entry *_head;
size_t _size;
};
}
#endif /* Spine_HashMap_h */

View File

@ -0,0 +1,118 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_IkConstraint_h
#define Spine_IkConstraint_h
#include <spine/ConstraintData.h>
#include <spine/Vector.h>
namespace spine {
class IkConstraintData;
class Skeleton;
class Bone;
class SP_API IkConstraint : public Updatable {
friend class Skeleton;
friend class IkConstraintTimeline;
RTTI_DECL
public:
/// Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified
/// in the world coordinate system.
static void
apply(Bone &bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha);
/// Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as
/// possible. The target is specified in the world coordinate system.
/// @param child A direct descendant of the parent bone.
static void
apply(Bone &parent, Bone &child, float targetX, float targetY, int bendDir, bool stretch, bool uniform,
float softness,
float alpha);
IkConstraint(IkConstraintData &data, Skeleton &skeleton);
virtual void update(Physics physics);
virtual int getOrder();
IkConstraintData &getData();
Vector<Bone *> &getBones();
Bone *getTarget();
void setTarget(Bone *inValue);
int getBendDirection();
void setBendDirection(int inValue);
bool getCompress();
void setCompress(bool inValue);
bool getStretch();
void setStretch(bool inValue);
float getMix();
void setMix(float inValue);
float getSoftness();
void setSoftness(float inValue);
bool isActive();
void setActive(bool inValue);
void setToSetupPose();
private:
IkConstraintData &_data;
Vector<Bone *> _bones;
int _bendDirection;
bool _compress;
bool _stretch;
float _mix;
float _softness;
Bone *_target;
bool _active;
};
}
#endif /* Spine_IkConstraint_h */

View File

@ -0,0 +1,102 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_IkConstraintData_h
#define Spine_IkConstraintData_h
#include <spine/Vector.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/ConstraintData.h>
namespace spine {
class BoneData;
class SP_API IkConstraintData : public ConstraintData {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class IkConstraint;
friend class Skeleton;
friend class IkConstraintTimeline;
public:
RTTI_DECL
explicit IkConstraintData(const String &name);
/// The bones that are constrained by this IK Constraint.
Vector<BoneData *> &getBones();
/// The bone that is the IK target.
BoneData *getTarget();
void setTarget(BoneData *inValue);
/// Controls the bend direction of the IK bones, either 1 or -1.
int getBendDirection();
void setBendDirection(int inValue);
bool getCompress();
void setCompress(bool inValue);
bool getStretch();
void setStretch(bool inValue);
bool getUniform();
void setUniform(bool inValue);
float getMix();
void setMix(float inValue);
float getSoftness();
void setSoftness(float inValue);
private:
Vector<BoneData *> _bones;
BoneData *_target;
int _bendDirection;
bool _compress;
bool _stretch;
bool _uniform;
float _mix;
float _softness;
};
}
#endif /* Spine_IkConstraintData_h */

View File

@ -0,0 +1,70 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_IkConstraintTimeline_h
#define Spine_IkConstraintTimeline_h
#include <spine/CurveTimeline.h>
namespace spine {
class SP_API IkConstraintTimeline : public CurveTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit IkConstraintTimeline(size_t frameCount, size_t bezierCount, int ikConstraintIndex);
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time, mix and bend direction of the specified keyframe.
void setFrame(int frame, float time, float mix, float softness, int bendDirection, bool compress, bool stretch);
int getIkConstraintIndex() { return _constraintIndex; }
void setIkConstraintIndex(int inValue) { _constraintIndex = inValue; }
private:
int _constraintIndex;
static const int ENTRIES = 6;
static const int MIX = 1;
static const int SOFTNESS = 2;
static const int BEND_DIRECTION = 3;
static const int COMPRESS = 4;
static const int STRETCH = 5;
};
}
#endif /* Spine_IkConstraintTimeline_h */

View File

@ -0,0 +1,43 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_TransformMode_h
#define Spine_TransformMode_h
namespace spine {
enum Inherit {
Inherit_Normal = 0,
Inherit_OnlyTranslation,
Inherit_NoRotationOrReflection,
Inherit_NoScale,
Inherit_NoScaleOrReflection
};
}
#endif /* Spine_TransformMode_h */

View File

@ -0,0 +1,71 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_InheritTimeline_h
#define Spine_InheritTimeline_h
#include <spine/Timeline.h>
#include <spine/Animation.h>
#include <spine/Property.h>
#include <spine/Inherit.h>
namespace spine {
class SP_API InheritTimeline : public Timeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit InheritTimeline(size_t frameCount, int boneIndex);
virtual ~InheritTimeline();
void setFrame(int frame, float time, Inherit inherit);
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getBoneIndex() { return _boneIndex; }
void setBoneIndex(int inValue) { _boneIndex = inValue; }
private:
int _boneIndex;
static const int ENTRIES = 2;
static const int INHERIT = 1;
};
}
#endif /* Spine_InheritTimeline_h */

View File

@ -0,0 +1,116 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Json_h
#define Spine_Json_h
#include <spine/SpineObject.h>
#ifndef SPINE_JSON_HAVE_PREV
/* spine doesn't use the "prev" link in the Json sibling lists. */
#define SPINE_JSON_HAVE_PREV 0
#endif
namespace spine {
class SP_API Json : public SpineObject {
friend class SkeletonJson;
public:
/* Json Types: */
static const int JSON_FALSE;
static const int JSON_TRUE;
static const int JSON_NULL;
static const int JSON_NUMBER;
static const int JSON_STRING;
static const int JSON_ARRAY;
static const int JSON_OBJECT;
/* Get item "string" from object. Case insensitive. */
static Json *getItem(Json *object, const char *string);
static Json *getItem(Json *object, int childIndex);
static const char *getString(Json *object, const char *name, const char *defaultValue);
static float getFloat(Json *object, const char *name, float defaultValue);
static int getInt(Json *object, const char *name, int defaultValue);
static bool getBoolean(Json *object, const char *name, bool defaultValue);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */
static const char *getError();
/* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */
explicit Json(const char *value);
~Json();
private:
static const char *_error;
Json *_next;
#if SPINE_JSON_HAVE_PREV
Json* _prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItem */
#endif
Json *_child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int _type; /* The type of the item, as above. */
int _size; /* The number of children. */
const char *_valueString; /* The item's string, if type==JSON_STRING */
int _valueInt; /* The item's number, if type==JSON_NUMBER */
float _valueFloat; /* The item's number, if type==JSON_NUMBER */
const char *_name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *inValue);
/* Parser core - when encountering text, process appropriately. */
static const char *parseValue(Json *item, const char *value);
/* Parse the input text into an unescaped cstring, and populate item. */
static const char *parseString(Json *item, const char *str);
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parseNumber(Json *item, const char *num);
/* Build an array from input text. */
static const char *parseArray(Json *item, const char *value);
/* Build an object from the text. */
static const char *parseObject(Json *item, const char *value);
static int json_strcasecmp(const char *s1, const char *s2);
};
}
#endif /* Spine_Json_h */

View File

@ -0,0 +1,61 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_LinkedMesh_h
#define Spine_LinkedMesh_h
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
namespace spine {
class MeshAttachment;
class SP_API LinkedMesh : public SpineObject {
friend class SkeletonBinary;
friend class SkeletonJson;
public:
LinkedMesh(MeshAttachment *mesh, const int skinIndex, size_t slotIndex, const String &parent,
bool inheritTimeline);
LinkedMesh(MeshAttachment *mesh, const String &skin, size_t slotIndex, const String &parent,
bool inheritTimeline);
private:
MeshAttachment *_mesh;
int _skinIndex;
String _skin;
size_t _slotIndex;
String _parent;
bool _inheritTimeline;
};
}
#endif /* Spine_LinkedMesh_h */

View File

@ -0,0 +1,57 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef SPINE_DEBUG_LOG_H
#define SPINE_DEBUG_LOG_H
#include <spine/spine.h>
namespace spine {
SP_API void spDebug_printSkeletonData(SkeletonData *skeletonData);
SP_API void spDebug_printAnimation(Animation *animation);
SP_API void spDebug_printTimeline(Timeline *timeline);
SP_API void spDebug_printBoneDatas(Vector<BoneData *> &boneDatas);
SP_API void spDebug_printBoneData(BoneData *boneData);
SP_API void spDebug_printSkeleton(Skeleton *skeleton);
SP_API void spDebug_printBones(Vector<Bone *> &bones);
SP_API void spDebug_printBone(Bone *bone);
SP_API void spDebug_printFloats(float *values, int numFloats);
SP_API void spDebug_printFloats(Vector<float> &values);
}
#endif

View File

@ -0,0 +1,139 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_MathUtil_h
#define Spine_MathUtil_h
#include <spine/SpineObject.h>
#include <string.h>
// Needed for older MSVC versions
#undef min
#undef max
namespace spine {
class SP_API MathUtil : public SpineObject {
private:
MathUtil();
public:
static const float Pi;
static const float Pi_2;
static const float InvPi_2;
static const float Deg_Rad;
static const float Rad_Deg;
template<typename T>
static inline T min(T a, T b) { return a < b ? a : b; }
template<typename T>
static inline T max(T a, T b) { return a > b ? a : b; }
static float sign(float val);
static float clamp(float x, float lower, float upper);
static float abs(float v);
/// Returns the sine in radians from a lookup table.
static float sin(float radians);
/// Returns the cosine in radians from a lookup table.
static float cos(float radians);
/// Returns the sine in radians from a lookup table.
static float sinDeg(float degrees);
/// Returns the cosine in radians from a lookup table.
static float cosDeg(float degrees);
/// Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323
/// degrees), largest error of 0.00488 radians (0.2796 degrees).
static float atan2(float y, float x);
static float atan2Deg(float x, float y);
static float acos(float v);
static float sqrt(float v);
static float fmod(float a, float b);
static bool isNan(float v);
static float quietNan();
static float random();
static float randomTriangular(float min, float max);
static float randomTriangular(float min, float max, float mode);
static float pow(float a, float b);
static float ceil(float v);
};
struct SP_API Interpolation {
virtual float apply(float a) = 0;
virtual float interpolate(float start, float end, float a) {
return start + (end - start) * apply(a);
}
virtual ~Interpolation() {};
};
struct SP_API PowInterpolation : public Interpolation {
PowInterpolation(int power) : power(power) {
}
float apply(float a) {
if (a <= 0.5f) return MathUtil::pow(a * 2.0f, (float) power) / 2.0f;
return MathUtil::pow((a - 1.0f) * 2.0f, (float) power) / (power % 2 == 0 ? -2.0f : 2.0f) + 1.0f;
}
int power;
};
struct SP_API PowOutInterpolation : public Interpolation {
PowOutInterpolation(int power) : power(power) {
}
float apply(float a) {
return MathUtil::pow(a - 1, (float) power) * (power % 2 == 0 ? -1.0f : 1.0f) + 1.0f;
}
int power;
};
}
#endif /* Spine_MathUtil_h */

View File

@ -0,0 +1,122 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_MeshAttachment_h
#define Spine_MeshAttachment_h
#include <spine/VertexAttachment.h>
#include <spine/TextureRegion.h>
#include <spine/Sequence.h>
#include <spine/Vector.h>
#include <spine/Color.h>
#include <spine/HasRendererObject.h>
namespace spine {
/// Attachment that displays a texture region using a mesh.
class SP_API MeshAttachment : public VertexAttachment {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class AtlasAttachmentLoader;
RTTI_DECL
public:
explicit MeshAttachment(const String &name);
virtual ~MeshAttachment();
using VertexAttachment::computeWorldVertices;
virtual void computeWorldVertices(Slot &slot, size_t start, size_t count, float *worldVertices, size_t offset,
size_t stride = 2);
void updateRegion();
int getHullLength();
void setHullLength(int inValue);
Vector<float> &getRegionUVs();
/// The UV pair for each vertex, normalized within the entire texture. See also MeshAttachment::updateRegion
Vector<float> &getUVs();
Vector<unsigned short> &getTriangles();
Color &getColor();
const String &getPath();
void setPath(const String &inValue);
TextureRegion *getRegion();
void setRegion(TextureRegion *region);
Sequence *getSequence();
void setSequence(Sequence *sequence);
MeshAttachment *getParentMesh();
void setParentMesh(MeshAttachment *inValue);
// Nonessential.
Vector<unsigned short> &getEdges();
float getWidth();
void setWidth(float inValue);
float getHeight();
void setHeight(float inValue);
virtual Attachment *copy();
MeshAttachment *newLinkedMesh();
private:
MeshAttachment *_parentMesh;
Vector<float> _uvs;
Vector<float> _regionUVs;
Vector<unsigned short> _triangles;
Vector<unsigned short> _edges;
String _path;
Color _color;
int _hullLength;
int _width, _height;
TextureRegion *_region;
Sequence *_sequence;
};
}
#endif /* Spine_MeshAttachment_h */

View File

@ -0,0 +1,45 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_MixPose_h
#define Spine_MixPose_h
namespace spine {
/// Controls how a timeline is mixed with the setup or current pose.
/// See also Timeline::apply(Skeleton&, float, float, Vector&, float, Blend, MixDirection)
enum MixBlend {
MixBlend_Setup = 0,
MixBlend_First,
MixBlend_Replace,
MixBlend_Add
};
}
#endif /* Spine_MixPose_h */

View File

@ -0,0 +1,44 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_MixDirection_h
#define Spine_MixDirection_h
namespace spine {
/// Indicates whether a timeline's alpha is mixing out over time toward 0 (the setup or current pose) or mixing in toward 1 (the timeline's pose).
/// See also Timeline::apply(Skeleton&, float, float, Vector&, float, MixPose, MixDirection)
enum MixDirection {
MixDirection_In = 0,
MixDirection_Out
};
}
#endif /* Spine_MixDirection_h */

View File

@ -0,0 +1,70 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PathAttachment_h
#define Spine_PathAttachment_h
#include <spine/VertexAttachment.h>
#include <spine/Color.h>
namespace spine {
class SP_API PathAttachment : public VertexAttachment {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PathAttachment(const String &name);
/// The length in the setup pose from the start of the path to the end of each curve.
Vector<float> &getLengths();
bool isClosed();
void setClosed(bool inValue);
bool isConstantSpeed();
void setConstantSpeed(bool inValue);
Color &getColor();
virtual Attachment *copy();
private:
Vector<float> _lengths;
bool _closed;
bool _constantSpeed;
Color _color;
};
}
#endif /* Spine_PathAttachment_h */

View File

@ -0,0 +1,133 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PathConstraint_h
#define Spine_PathConstraint_h
#include <spine/ConstraintData.h>
#include <spine/Vector.h>
namespace spine {
class PathConstraintData;
class Skeleton;
class PathAttachment;
class Bone;
class Slot;
class SP_API PathConstraint : public Updatable {
friend class Skeleton;
friend class PathConstraintMixTimeline;
friend class PathConstraintPositionTimeline;
friend class PathConstraintSpacingTimeline;
RTTI_DECL
public:
PathConstraint(PathConstraintData &data, Skeleton &skeleton);
virtual void update(Physics physics);
virtual int getOrder();
PathConstraintData &getData();
Vector<Bone *> &getBones();
Slot *getTarget();
void setTarget(Slot *inValue);
float getPosition();
void setPosition(float inValue);
float getSpacing();
void setSpacing(float inValue);
float getMixRotate();
void setMixRotate(float inValue);
float getMixX();
void setMixX(float inValue);
float getMixY();
void setMixY(float inValue);
bool isActive();
void setActive(bool inValue);
void setToSetupPose();
private:
static const float EPSILON;
static const int NONE;
static const int BEFORE;
static const int AFTER;
PathConstraintData &_data;
Vector<Bone *> _bones;
Slot *_target;
float _position, _spacing;
float _mixRotate, _mixX, _mixY;
Vector<float> _spaces;
Vector<float> _positions;
Vector<float> _world;
Vector<float> _curves;
Vector<float> _lengths;
Vector<float> _segments;
bool _active;
Vector<float> &computeWorldPositions(PathAttachment &path, int spacesCount, bool tangents);
static void addBeforePosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o);
static void addAfterPosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o);
static void
addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
Vector<float> &output, int o, bool tangents);
};
}
#endif /* Spine_PathConstraint_h */

View File

@ -0,0 +1,119 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PathConstraintData_h
#define Spine_PathConstraintData_h
#include <spine/PositionMode.h>
#include <spine/SpacingMode.h>
#include <spine/RotateMode.h>
#include <spine/Vector.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/ConstraintData.h>
namespace spine {
class BoneData;
class SlotData;
class SP_API PathConstraintData : public ConstraintData {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class PathConstraint;
friend class Skeleton;
friend class PathConstraintMixTimeline;
friend class PathConstraintPositionTimeline;
friend class PathConstraintSpacingTimeline;
public:
RTTI_DECL
explicit PathConstraintData(const String &name);
Vector<BoneData *> &getBones();
SlotData *getTarget();
void setTarget(SlotData *inValue);
PositionMode getPositionMode();
void setPositionMode(PositionMode inValue);
SpacingMode getSpacingMode();
void setSpacingMode(SpacingMode inValue);
RotateMode getRotateMode();
void setRotateMode(RotateMode inValue);
float getOffsetRotation();
void setOffsetRotation(float inValue);
float getPosition();
void setPosition(float inValue);
float getSpacing();
void setSpacing(float inValue);
float getMixRotate();
void setMixRotate(float inValue);
float getMixX();
void setMixX(float inValue);
float getMixY();
void setMixY(float inValue);
private:
Vector<BoneData *> _bones;
SlotData *_target;
PositionMode _positionMode;
SpacingMode _spacingMode;
RotateMode _rotateMode;
float _offsetRotation;
float _position, _spacing;
float _mixRotate, _mixX, _mixY;
};
}
#endif /* Spine_PathConstraintData_h */

View File

@ -0,0 +1,68 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PathConstraintMixTimeline_h
#define Spine_PathConstraintMixTimeline_h
#include <spine/CurveTimeline.h>
namespace spine {
class SP_API PathConstraintMixTimeline : public CurveTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PathConstraintMixTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex);
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
/// Sets the time and mixes of the specified keyframe.
void setFrame(int frameIndex, float time, float mixRotate, float mixX, float mixY);
int getPathConstraintIndex() { return _constraintIndex; }
void setPathConstraintIndex(int inValue) { _constraintIndex = inValue; }
private:
int _constraintIndex;
static const int ENTRIES = 4;
static const int ROTATE = 1;
static const int X = 2;
static const int Y = 3;
};
}
#endif /* Spine_PathConstraintMixTimeline_h */

View File

@ -0,0 +1,64 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PathConstraintPositionTimeline_h
#define Spine_PathConstraintPositionTimeline_h
#include <spine/CurveTimeline.h>
namespace spine {
class SP_API PathConstraintPositionTimeline : public CurveTimeline1 {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
static const int ENTRIES;
explicit PathConstraintPositionTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex);
virtual ~PathConstraintPositionTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getPathConstraintIndex() { return _constraintIndex; }
void setPathConstraintIndex(int inValue) { _constraintIndex = inValue; }
protected:
int _constraintIndex;
};
}
#endif /* Spine_PathConstraintPositionTimeline_h */

View File

@ -0,0 +1,59 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PathConstraintSpacingTimeline_h
#define Spine_PathConstraintSpacingTimeline_h
#include <spine/PathConstraintPositionTimeline.h>
namespace spine {
class SP_API PathConstraintSpacingTimeline : public CurveTimeline1 {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PathConstraintSpacingTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex);
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getPathConstraintIndex() { return _pathConstraintIndex; }
void setPathConstraintIndex(int inValue) { _pathConstraintIndex = inValue; }
protected:
int _pathConstraintIndex;
};
}
#endif /* Spine_PathConstraintSpacingTimeline_h */

View File

@ -0,0 +1,50 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Physics_h
#define Spine_Physics_h
/** Determines how physics and other non-deterministic updates are applied. */
namespace spine {
enum Physics {
/** Physics are not updated or applied. */
Physics_None,
/** Physics are reset to the current pose. */
Physics_Reset,
/** Physics are updated and the pose from physics is applied. */
Physics_Update,
/** Physics are not updated but the pose from physics is applied. */
Physics_Pose
};
}
#endif

View File

@ -0,0 +1,197 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PhysicsConstraint_h
#define Spine_PhysicsConstraint_h
#include <spine/ConstraintData.h>
#include <spine/Vector.h>
namespace spine {
class PhysicsConstraintData;
class Skeleton;
class Bone;
class SP_API PhysicsConstraint : public Updatable {
friend class Skeleton;
friend class PhysicsConstraintTimeline;
friend class PhysicsConstraintInertiaTimeline;
friend class PhysicsConstraintStrengthTimeline;
friend class PhysicsConstraintDampingTimeline;
friend class PhysicsConstraintMassTimeline;
friend class PhysicsConstraintWindTimeline;
friend class PhysicsConstraintGravityTimeline;
friend class PhysicsConstraintMixTimeline;
friend class PhysicsConstraintResetTimeline;
RTTI_DECL
public:
PhysicsConstraint(PhysicsConstraintData& data, Skeleton& skeleton);
PhysicsConstraintData &getData();
void setBone(Bone* bone);
Bone* getBone();
void setInertia(float value);
float getInertia();
void setStrength(float value);
float getStrength();
void setDamping(float value);
float getDamping();
void setMassInverse(float value);
float getMassInverse();
void setWind(float value);
float getWind();
void setGravity(float value);
float getGravity();
void setMix(float value);
float getMix();
void setReset(bool value);
bool getReset();
void setUx(float value);
float getUx();
void setUy(float value);
float getUy();
void setCx(float value);
float getCx();
void setCy(float value);
float getCy();
void setTx(float value);
float getTx();
void setTy(float value);
float getTy();
void setXOffset(float value);
float getXOffset();
void setXVelocity(float value);
float getXVelocity();
void setYOffset(float value);
float getYOffset();
void setYVelocity(float value);
float getYVelocity();
void setRotateOffset(float value);
float getRotateOffset();
void setRotateVelocity(float value);
float getRotateVelocity();
void setScaleOffset(float value);
float getScaleOffset();
void setScaleVelocity(float value);
float getScaleVelocity();
void setActive(bool value);
bool isActive();
void setRemaining(float value);
float getRemaining();
void setLastTime(float value);
float getLastTime();
void reset();
void setToSetupPose();
virtual void update(Physics physics);
void translate(float x, float y);
void rotate(float x, float y, float degrees);
private:
PhysicsConstraintData& _data;
Bone* _bone;
float _inertia;
float _strength;
float _damping;
float _massInverse;
float _wind;
float _gravity;
float _mix;
bool _reset;
float _ux;
float _uy;
float _cx;
float _cy;
float _tx;
float _ty;
float _xOffset;
float _xVelocity;
float _yOffset;
float _yVelocity;
float _rotateOffset;
float _rotateVelocity;
float _scaleOffset;
float _scaleVelocity;
bool _active;
Skeleton& _skeleton;
float _remaining;
float _lastTime;
};
}
#endif /* Spine_PhysicsConstraint_h */

View File

@ -0,0 +1,151 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PhysicsConstraintData_h
#define Spine_PhysicsConstraintData_h
#include <spine/Vector.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <spine/ConstraintData.h>
namespace spine {
class BoneData;
class SP_API PhysicsConstraintData : public ConstraintData {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class Skeleton;
friend class PhysicsConstraint;
public:
RTTI_DECL
explicit PhysicsConstraintData(const String &name);
void setBone(BoneData* bone);
BoneData* getBone() const;
void setX(float x);
float getX() const;
void setY(float y);
float getY() const;
void setRotate(float rotate);
float getRotate() const;
void setScaleX(float scaleX);
float getScaleX() const;
void setShearX(float shearX);
float getShearX() const;
void setLimit(float limit);
float getLimit() const;
void setStep(float step);
float getStep() const;
void setInertia(float inertia);
float getInertia() const;
void setStrength(float strength);
float getStrength() const;
void setDamping(float damping);
float getDamping() const;
void setMassInverse(float massInverse);
float getMassInverse() const;
void setWind(float wind);
float getWind() const;
void setGravity(float gravity);
float getGravity() const;
void setMix(float mix);
float getMix() const;
void setInertiaGlobal(bool inertiaGlobal);
bool isInertiaGlobal() const;
void setStrengthGlobal(bool strengthGlobal);
bool isStrengthGlobal() const;
void setDampingGlobal(bool dampingGlobal);
bool isDampingGlobal() const;
void setMassGlobal(bool massGlobal);
bool isMassGlobal() const;
void setWindGlobal(bool windGlobal);
bool isWindGlobal() const;
void setGravityGlobal(bool gravityGlobal);
bool isGravityGlobal() const;
void setMixGlobal(bool mixGlobal);
bool isMixGlobal() const;
private:
BoneData *_bone;
float _x, _y, _rotate, _scaleX, _shearX, _limit;
float _step, _inertia, _strength, _damping, _massInverse, _wind, _gravity, _mix;
bool _inertiaGlobal, _strengthGlobal, _dampingGlobal, _massGlobal, _windGlobal, _gravityGlobal, _mixGlobal;
};
}
#endif /* Spine_PhysicsConstraintData_h */

View File

@ -0,0 +1,288 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PhysicsConstraintTimeline_h
#define Spine_PhysicsConstraintTimeline_h
#include <spine/CurveTimeline.h>
#include <spine/PhysicsConstraint.h>
#include <spine/PhysicsConstraintData.h>
namespace spine {
class SP_API PhysicsConstraintTimeline : public CurveTimeline1 {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex, Property property);
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getPhysicsConstraintIndex() { return _constraintIndex; }
void setPhysicsConstraintIndex(int inValue) { _constraintIndex = inValue; }
protected:
virtual float setup(PhysicsConstraint *constraint) = 0;
virtual float get(PhysicsConstraint *constraint) = 0;
virtual void set(PhysicsConstraint *constraint, float value) = 0;
virtual bool global(PhysicsConstraintData &constraintData) = 0;
private:
int _constraintIndex;
};
class SP_API PhysicsConstraintInertiaTimeline : public PhysicsConstraintTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintInertiaTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintInertia) {};
protected:
float setup(PhysicsConstraint *constraint) {
return constraint->_data.getInertia();
}
float get(PhysicsConstraint *constraint) {
return constraint->_inertia;
}
void set(PhysicsConstraint *constraint, float value) {
constraint->_inertia = value;
}
bool global(PhysicsConstraintData &constraintData) {
return constraintData.isInertiaGlobal();
}
};
class SP_API PhysicsConstraintStrengthTimeline : public PhysicsConstraintTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintStrengthTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintStrength) {};
protected:
float setup(PhysicsConstraint *constraint) {
return constraint->_data.getStrength();
}
float get(PhysicsConstraint *constraint) {
return constraint->_strength;
}
void set(PhysicsConstraint *constraint, float value) {
constraint->_strength = value;
}
bool global(PhysicsConstraintData &constraintData) {
return constraintData.isStrengthGlobal();
}
};
class SP_API PhysicsConstraintDampingTimeline : public PhysicsConstraintTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintDampingTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintDamping) {};
protected:
float setup(PhysicsConstraint *constraint) {
return constraint->_data.getDamping();
}
float get(PhysicsConstraint *constraint) {
return constraint->_damping;
}
void set(PhysicsConstraint *constraint, float value) {
constraint->_damping = value;
}
bool global(PhysicsConstraintData &constraintData) {
return constraintData.isDampingGlobal();
}
};
class SP_API PhysicsConstraintMassTimeline : public PhysicsConstraintTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintMassTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintMass) {};
protected:
float setup(PhysicsConstraint *constraint) {
return 1 / constraint->_data.getMassInverse();
}
float get(PhysicsConstraint *constraint) {
return 1 / constraint->_massInverse;
}
void set(PhysicsConstraint *constraint, float value) {
constraint->_massInverse = 1 / value;
}
bool global(PhysicsConstraintData &constraintData) {
return constraintData.isMassGlobal();
}
};
class SP_API PhysicsConstraintWindTimeline : public PhysicsConstraintTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintWindTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintWind) {};
protected:
float setup(PhysicsConstraint *constraint) {
return constraint->_data.getWind();
}
float get(PhysicsConstraint *constraint) {
return constraint->_wind;
}
void set(PhysicsConstraint *constraint, float value) {
constraint->_wind = value;
}
bool global(PhysicsConstraintData &constraintData) {
return constraintData.isWindGlobal();
}
};
class SP_API PhysicsConstraintGravityTimeline : public PhysicsConstraintTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintGravityTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintGravity) {};
protected:
float setup(PhysicsConstraint *constraint) {
return constraint->_data.getGravity();
}
float get(PhysicsConstraint *constraint) {
return constraint->_gravity;
}
void set(PhysicsConstraint *constraint, float value) {
constraint->_gravity = value;
}
bool global(PhysicsConstraintData &constraintData) {
return constraintData.isGravityGlobal();
}
};
class SP_API PhysicsConstraintMixTimeline : public PhysicsConstraintTimeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintMixTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintMix) {};
protected:
float setup(PhysicsConstraint *constraint) {
return constraint->_data.getMix();
}
float get(PhysicsConstraint *constraint) {
return constraint->_mix;
}
void set(PhysicsConstraint *constraint, float value) {
constraint->_mix = value;
}
bool global(PhysicsConstraintData &constraintData) {
return constraintData.isMixGlobal();
}
};
class SP_API PhysicsConstraintResetTimeline : public Timeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PhysicsConstraintResetTimeline(size_t frameCount, int physicsConstraintIndex): Timeline(frameCount, 1), _constraintIndex(physicsConstraintIndex) {
PropertyId ids[] = {((PropertyId)Property_PhysicsConstraintReset) << 32};
setPropertyIds(ids, 1);
}
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
void setFrame(int frame, float time) {
_frames[frame] = time;
}
private:
int _constraintIndex;
};
}
#endif /* Spine_PhysicsConstraintTimeline_h */

View File

@ -0,0 +1,81 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PointAttachment_h
#define Spine_PointAttachment_h
#include <spine/Attachment.h>
#include <spine/Color.h>
namespace spine {
class Bone;
/// An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be
/// used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a
/// skin.
///
/// See http://esotericsoftware.com/spine-point-attachments for Point Attachments in the Spine User Guide.
///
class SP_API PointAttachment : public Attachment {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit PointAttachment(const String &name);
void computeWorldPosition(Bone &bone, float &ox, float &oy);
float computeWorldRotation(Bone &bone);
float getX();
void setX(float inValue);
float getY();
void setY(float inValue);
float getRotation();
void setRotation(float inValue);
Color &getColor();
virtual Attachment *copy();
private:
float _x, _y, _rotation;
Color _color;
};
}
#endif /* Spine_PointAttachment_h */

View File

@ -0,0 +1,74 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Pool_h
#define Spine_Pool_h
#include <spine/Extension.h>
#include <spine/Vector.h>
#include <spine/ContainerUtil.h>
#include <spine/SpineObject.h>
namespace spine {
template<typename T>
class SP_API Pool : public SpineObject {
public:
Pool() {
}
~Pool() {
ContainerUtil::cleanUpVectorOfPointers(_objects);
}
T *obtain() {
if (_objects.size() > 0) {
T **object = &_objects[_objects.size() - 1];
T *ret = *object;
_objects.removeAt(_objects.size() - 1);
return ret;
} else {
T *ret = new(__FILE__, __LINE__) T();
return ret;
}
}
void free(T *object) {
if (!_objects.contains(object)) {
_objects.add(object);
}
}
private:
Vector<T *> _objects;
};
}
#endif /* Spine_Pool_h */

View File

@ -0,0 +1,40 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_PositionMode_h
#define Spine_PositionMode_h
namespace spine {
enum PositionMode {
PositionMode_Fixed = 0,
PositionMode_Percent
};
}
#endif /* Spine_PositionMode_h */

View File

@ -0,0 +1,68 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Property_h
#define Spine_Property_h
namespace spine {
typedef long long PropertyId;
enum Property {
Property_Rotate = 1 << 0,
Property_X = 1 << 1,
Property_Y = 1 << 2,
Property_ScaleX = 1 << 3,
Property_ScaleY = 1 << 4,
Property_ShearX = 1 << 5,
Property_ShearY = 1 << 6,
Property_Inherit = 1 << 7,
Property_Rgb = 1 << 8,
Property_Alpha = 1 << 9,
Property_Rgb2 = 1 << 10,
Property_Attachment = 1 << 11,
Property_Deform = 1 << 12,
Property_Event = 1 << 13,
Property_DrawOrder = 1 << 14,
Property_IkConstraint = 1 << 15,
Property_TransformConstraint = 1 << 16,
Property_PathConstraintPosition = 1 << 17,
Property_PathConstraintSpacing = 1 << 18,
Property_PathConstraintMix = 1 << 19,
Property_PhysicsConstraintInertia = 1 << 20,
Property_PhysicsConstraintStrength = 1 << 21,
Property_PhysicsConstraintDamping = 1 << 22,
Property_PhysicsConstraintMass = 1 << 23,
Property_PhysicsConstraintWind = 1 << 24,
Property_PhysicsConstraintGravity = 1 << 25,
Property_PhysicsConstraintMix = 1 << 26,
Property_PhysicsConstraintReset = 1 << 27,
Property_Sequence = 1 << 28
};
}
#endif /* Spine_Property_h */

View File

@ -0,0 +1,72 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_RTTI_h
#define Spine_RTTI_h
#include <spine/dll.h>
namespace spine {
class SP_API RTTI {
public:
explicit RTTI(const char *className);
RTTI(const char *className, const RTTI &baseRTTI);
const char *getClassName() const;
bool isExactly(const RTTI &rtti) const;
bool instanceOf(const RTTI &rtti) const;
private:
// Prevent copying
RTTI(const RTTI &obj);
RTTI &operator=(const RTTI &obj);
const char *_className;
const RTTI *_pBaseRTTI;
};
}
#define RTTI_DECL \
public: \
static const spine::RTTI rtti; \
virtual const spine::RTTI& getRTTI() const;
#define RTTI_IMPL_NOPARENT(name) \
const spine::RTTI name::rtti(#name); \
const spine::RTTI& name::getRTTI() const { return rtti; }
#define RTTI_IMPL(name, parent) \
const spine::RTTI name::rtti(#name, parent::rtti); \
const spine::RTTI& name::getRTTI() const { return rtti; }
#endif /* Spine_RTTI_h */

View File

@ -0,0 +1,140 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_RegionAttachment_h
#define Spine_RegionAttachment_h
#include <spine/Attachment.h>
#include <spine/Vector.h>
#include <spine/Color.h>
#include <spine/Sequence.h>
#include <spine/TextureRegion.h>
#include <spine/HasRendererObject.h>
#define NUM_UVS 8
namespace spine {
class Bone;
/// Attachment that displays a texture region.
class SP_API RegionAttachment : public Attachment {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class AtlasAttachmentLoader;
RTTI_DECL
public:
explicit RegionAttachment(const String &name);
virtual ~RegionAttachment();
void updateRegion();
/// Transforms the attachment's four vertices to world coordinates.
/// @param slot The parent slot.
/// @param worldVertices The output world vertices. Must have a length greater than or equal to offset + 8.
/// @param offset The worldVertices index to begin writing values.
/// @param stride The number of worldVertices entries between the value pairs written.
void computeWorldVertices(Slot &slot, float *worldVertices, size_t offset, size_t stride = 2);
void computeWorldVertices(Slot &slot, Vector<float> &worldVertices, size_t offset, size_t stride = 2);
float getX();
void setX(float inValue);
float getY();
void setY(float inValue);
float getRotation();
void setRotation(float inValue);
float getScaleX();
void setScaleX(float inValue);
float getScaleY();
void setScaleY(float inValue);
float getWidth();
void setWidth(float inValue);
float getHeight();
void setHeight(float inValue);
Color &getColor();
const String &getPath();
void setPath(const String &inValue);
TextureRegion *getRegion();
void setRegion(TextureRegion *region);
Sequence *getSequence();
void setSequence(Sequence *sequence);
Vector<float> &getOffset();
Vector<float> &getUVs();
virtual Attachment *copy();
private:
static const int BLX;
static const int BLY;
static const int ULX;
static const int ULY;
static const int URX;
static const int URY;
static const int BRX;
static const int BRY;
float _x, _y, _rotation, _scaleX, _scaleY, _width, _height;
Vector<float> _vertexOffset;
Vector<float> _uvs;
String _path;
Color _color;
TextureRegion *_region;
Sequence *_sequence;
};
}
#endif /* Spine_RegionAttachment_h */

View File

@ -0,0 +1,41 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_RotateMode_h
#define Spine_RotateMode_h
namespace spine {
enum RotateMode {
RotateMode_Tangent = 0,
RotateMode_Chain,
RotateMode_ChainScale
};
}
#endif /* Spine_RotateMode_h */

View File

@ -0,0 +1,61 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_RotateTimeline_h
#define Spine_RotateTimeline_h
#include <spine/CurveTimeline.h>
namespace spine {
class SP_API RotateTimeline : public CurveTimeline1 {
friend class SkeletonBinary;
friend class SkeletonJson;
friend class AnimationState;
RTTI_DECL
public:
explicit RotateTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getBoneIndex() { return _boneIndex; }
void setBoneIndex(int inValue) { _boneIndex = inValue; }
private:
int _boneIndex;
};
}
#endif /* Spine_RotateTimeline_h */

View File

@ -0,0 +1,109 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_ScaleTimeline_h
#define Spine_ScaleTimeline_h
#include <spine/TranslateTimeline.h>
namespace spine {
class SP_API ScaleTimeline : public CurveTimeline2 {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit ScaleTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
virtual ~ScaleTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getBoneIndex() { return _boneIndex; }
void setBoneIndex(int inValue) { _boneIndex = inValue; }
private:
int _boneIndex;
};
class SP_API ScaleXTimeline : public CurveTimeline1 {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit ScaleXTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
virtual ~ScaleXTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getBoneIndex() { return _boneIndex; }
void setBoneIndex(int inValue) { _boneIndex = inValue; }
private:
int _boneIndex;
};
class SP_API ScaleYTimeline : public CurveTimeline1 {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit ScaleYTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
virtual ~ScaleYTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
int getBoneIndex() { return _boneIndex; }
void setBoneIndex(int inValue) { _boneIndex = inValue; }
private:
int _boneIndex;
};
}
#endif /* Spine_ScaleTimeline_h */

View File

@ -0,0 +1,98 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Sequence_h
#define Spine_Sequence_h
#include <spine/Vector.h>
#include <spine/SpineString.h>
#include <spine/TextureRegion.h>
namespace spine {
class Slot;
class Attachment;
class SkeletonBinary;
class SkeletonJson;
class SP_API Sequence : public SpineObject {
friend class SkeletonBinary;
friend class SkeletonJson;
public:
Sequence(int count);
~Sequence();
Sequence *copy();
void apply(Slot *slot, Attachment *attachment);
String getPath(const String &basePath, int index);
int getId() { return _id; }
void setId(int id) { _id = id; }
int getStart() { return _start; }
void setStart(int start) { _start = start; }
int getDigits() { return _digits; }
void setDigits(int digits) { _digits = digits; }
int getSetupIndex() { return _setupIndex; }
void setSetupIndex(int setupIndex) { _setupIndex = setupIndex; }
Vector<TextureRegion *> &getRegions() { return _regions; }
private:
int _id;
Vector<TextureRegion *> _regions;
int _start;
int _digits;
int _setupIndex;
int getNextID();
};
enum SequenceMode {
hold = 0,
once = 1,
loop = 2,
pingpong = 3,
onceReverse = 4,
loopReverse = 5,
pingpongReverse = 6
};
}
#endif

View File

@ -0,0 +1,73 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_SequenceTimeline_h
#define Spine_SequenceTimeline_h
#include <spine/Timeline.h>
#include <spine/Sequence.h>
namespace spine {
class Attachment;
class SP_API SequenceTimeline : public Timeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
explicit SequenceTimeline(size_t frameCount, int slotIndex, spine::Attachment *attachment);
virtual ~SequenceTimeline();
virtual void
apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
MixDirection direction);
void setFrame(int frame, float time, SequenceMode mode, int index, float delay);
int getSlotIndex() { return _slotIndex; };
void setSlotIndex(int inValue) { _slotIndex = inValue; }
Attachment *getAttachment() { return _attachment; }
protected:
int _slotIndex;
Attachment *_attachment;
static const int ENTRIES = 3;
static const int MODE = 1;
static const int DELAY = 2;
};
}
#endif /* Spine_SequenceTimeline_h */

Some files were not shown because too many files have changed in this diff Show More