Skip to content

Commit

Permalink
Add support for Spine objects (2D skeletal animation) (#5927)
Browse files Browse the repository at this point in the history
* This allows to use animatable 2D objects created with Spine (purchase a licence on [their website](https://esotericsoftware.com/) if you're interested).
* 2D skeletal animation allows for very smooth animations, and keep resource usage low compared to animations made of frames like Sprite objects. It's perfect for 2D games and can be used for animated characters, avatars, UI elements.
* Many thanks to @f0nar and @LousyMolars for their work on this new feature and associated game examples!

---------

Co-authored-by: Vladyslav Pohorielov <[email protected]>
Co-authored-by: Gleb Volkov <[email protected]>
Co-authored-by: Florian Rival <[email protected]>
Co-authored-by: Davy Hélard <[email protected]>
  • Loading branch information
5 people authored Jan 17, 2024
1 parent f623b35 commit d0005ba
Show file tree
Hide file tree
Showing 82 changed files with 11,786 additions and 30,159 deletions.
2 changes: 2 additions & 0 deletions Core/GDCore/Events/CodeGeneration/EventsCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,8 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
metadata.GetType() == "tilesetResource" ||
metadata.GetType() == "videoResource" ||
metadata.GetType() == "model3DResource" ||
metadata.GetType() == "atlasResource" ||
metadata.GetType() == "spineResource" ||
// Deprecated, old parameter names:
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
metadata.GetType() == "soundfile" || metadata.GetType() == "police") {
Expand Down
4 changes: 3 additions & 1 deletion Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ class GD_CORE_API ValueTypeMetadata {
parameterType == "jsonResource" ||
parameterType == "tilemapResource" ||
parameterType == "tilesetResource" ||
parameterType == "model3DResource";
parameterType == "model3DResource" ||
parameterType == "atlasResource" ||
parameterType == "spineResource";
}
return false;
}
Expand Down
27 changes: 27 additions & 0 deletions Core/GDCore/IDE/Project/ArbitraryResourceWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ void ArbitraryResourceWorker::ExposeModel3D(gd::String& resourceName){
// do.
};

void ArbitraryResourceWorker::ExposeAtlas(gd::String& resourceName){
// Nothing to do by default - each child class can define here the action to
// do.
};

void ArbitraryResourceWorker::ExposeSpine(gd::String& resourceName){
// Nothing to do by default - each child class can define here the action to
// do.
};

void ArbitraryResourceWorker::ExposeVideo(gd::String& videoName){
// Nothing to do by default - each child class can define here the action to
// do.
Expand Down Expand Up @@ -120,6 +130,7 @@ void ArbitraryResourceWorker::ExposeEmbeddeds(gd::String& resourceName) {

gd::String potentiallyUpdatedTargetResourceName = targetResourceName;
ExposeResourceWithType(targetResource.GetKind(), potentiallyUpdatedTargetResourceName);
ExposeEmbeddeds(potentiallyUpdatedTargetResourceName);

if (potentiallyUpdatedTargetResourceName != targetResourceName) {
// The resource name was renamed. Also update the mapping.
Expand Down Expand Up @@ -176,6 +187,14 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
ExposeVideo(resourceName);
return;
}
if (resourceType == "atlas") {
ExposeAtlas(resourceName);
return;
}
if (resourceType == "spine") {
ExposeSpine(resourceName);
return;
}
gd::LogError("Unexpected resource type: " + resourceType + " for: " + resourceName);
return;
}
Expand Down Expand Up @@ -244,6 +263,14 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
gd::String updatedParameterValue = parameterValue;
worker.ExposeModel3D(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "atlasResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeAtlas(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "spineResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeSpine(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
}
});

Expand Down
10 changes: 10 additions & 0 deletions Core/GDCore/IDE/Project/ArbitraryResourceWorker.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ class GD_CORE_API ArbitraryResourceWorker {
* \brief Expose a 3D model, which is always a reference to a "model3D" resource.
*/
virtual void ExposeModel3D(gd::String &resourceName);

/**
* \brief Expose an atlas, which is always a reference to a "atlas" resource.
*/
virtual void ExposeAtlas(gd::String &resourceName);

/**
* \brief Expose an spine, which is always a reference to a "spine" resource.
*/
virtual void ExposeSpine(gd::String &resourceName);

/**
* \brief Expose a video, which is always a reference to a "video" resource.
Expand Down
6 changes: 6 additions & 0 deletions Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ class GD_CORE_API ResourceNameMatcher : public ArbitraryResourceWorker {
virtual void ExposeModel3D(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeAtlas(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeSpine(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};

void MatchResourceName(gd::String& otherResourceName) {
if (otherResourceName == resourceName) matchesResourceName = true;
Expand Down
12 changes: 12 additions & 0 deletions Core/GDCore/IDE/Project/ResourcesInUseHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
std::set<gd::String>& GetAllVideos() { return GetAll("video"); };
std::set<gd::String>& GetAllBitmapFonts() { return GetAll("bitmapFont"); };
std::set<gd::String>& GetAll3DModels() { return GetAll("model3D"); };
std::set<gd::String>& GetAllAtlases() { return GetAll("atlas"); };
std::set<gd::String>& GetAllSpines() { return GetAll("spine"); };
std::set<gd::String>& GetAll(const gd::String& resourceType) {
if (resourceType == "image") return allImages;
if (resourceType == "audio") return allAudios;
Expand All @@ -56,6 +58,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
if (resourceType == "video") return allVideos;
if (resourceType == "bitmapFont") return allBitmapFonts;
if (resourceType == "model3D") return allModel3Ds;
if (resourceType == "atlas") return allAtlases;
if (resourceType == "spine") return allSpines;

return emptyResources;
};
Expand Down Expand Up @@ -90,6 +94,12 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
virtual void ExposeModel3D(gd::String& resourceName) override {
allModel3Ds.insert(resourceName);
};
virtual void ExposeAtlas(gd::String& resourceName) override {
allAtlases.insert(resourceName);
};
virtual void ExposeSpine(gd::String& resourceName) override {
allSpines.insert(resourceName);
};

protected:
std::vector<gd::String> allResources;
Expand All @@ -102,6 +112,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
std::set<gd::String> allVideos;
std::set<gd::String> allBitmapFonts;
std::set<gd::String> allModel3Ds;
std::set<gd::String> allAtlases;
std::set<gd::String> allSpines;
std::set<gd::String> emptyResources;

static const std::vector<gd::String> resourceTypes;
Expand Down
6 changes: 6 additions & 0 deletions Core/GDCore/IDE/Project/ResourcesRenamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
virtual void ExposeModel3D(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
};
virtual void ExposeAtlas(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
};
virtual void ExposeSpine(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
};

private:
void RenameIfNeeded(gd::String& resourceName) {
Expand Down
6 changes: 6 additions & 0 deletions Core/GDCore/IDE/Project/SceneResourcesFinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ class SceneResourcesFinder : private gd::ArbitraryResourceWorker {
void ExposeModel3D(gd::String &resourceName) override {
AddUsedResource(resourceName);
};
void ExposeAtlas(gd::String &resourceName) override {
AddUsedResource(resourceName);
};
void ExposeSpine(gd::String &resourceName) override {
AddUsedResource(resourceName);
};

std::set<gd::String> resourceNames;
};
Expand Down
4 changes: 4 additions & 0 deletions Core/GDCore/Project/CustomObjectConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
worker.ExposeBitmapFont(newPropertyValue);
} else if (resourceType == "model3D") {
worker.ExposeModel3D(newPropertyValue);
} else if (resourceType == "atlas") {
worker.ExposeAtlas(newPropertyValue);
} else if (resourceType == "spine") {
worker.ExposeSpine(newPropertyValue);
}

if (newPropertyValue != oldPropertyValue) {
Expand Down
18 changes: 18 additions & 0 deletions Core/GDCore/Project/ResourcesManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
return std::make_shared<BitmapFontResource>();
else if (kind == "model3D")
return std::make_shared<Model3DResource>();
else if (kind == "atlas")
return std::make_shared<AtlasResource>();
else if (kind == "spine")
return std::make_shared<SpineResource>();

std::cout << "Bad resource created (type: " << kind << ")" << std::endl;
return std::make_shared<Resource>();
Expand Down Expand Up @@ -756,6 +760,20 @@ void Model3DResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("file", GetFile());
}

void AtlasResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}

void AtlasResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}

void AtlasResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
}

ResourceFolder::ResourceFolder(const ResourceFolder& other) { Init(other); }

ResourceFolder& ResourceFolder::operator=(const ResourceFolder& other) {
Expand Down
41 changes: 41 additions & 0 deletions Core/GDCore/Project/ResourcesManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,21 @@ class GD_CORE_API JsonResource : public Resource {
gd::String file;
};

/**
* \brief Describe a spine json file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API SpineResource : public JsonResource {
public:
SpineResource() : JsonResource() { SetKind("spine"); };
virtual ~SpineResource(){};
virtual SpineResource* Clone() const override {
return new SpineResource(*this);
}
};

/**
* \brief Describe a tilemap file used by a project.
*
Expand Down Expand Up @@ -507,6 +522,32 @@ class GD_CORE_API Model3DResource : public Resource {
gd::String file;
};

/**
* \brief Describe an atlas file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API AtlasResource : public Resource {
public:
AtlasResource() : Resource() { SetKind("atlas"); };
virtual ~AtlasResource(){};
virtual AtlasResource* Clone() const override {
return new AtlasResource(*this);
}

virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;

virtual bool UseFile() const override { return true; }
void SerializeTo(SerializerElement& element) const override;

void UnserializeFrom(const SerializerElement& element) override;

private:
gd::String file;
};

/**
* \brief Inventory all resources used by a project
*
Expand Down
1 change: 1 addition & 0 deletions Extensions/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Spine/pixi-spine
1 change: 1 addition & 0 deletions Extensions/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ set(
TextEntryObject
TextObject
TiledSpriteObject
Spine
TopDownMovementBehavior)

# Automatically add all listed extensions
Expand Down
20 changes: 20 additions & 0 deletions Extensions/Spine/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 2.6)
cmake_policy(SET CMP0015 NEW)

project(SpineObject)
gd_add_extension_includes()

#Defines
###
gd_add_extension_definitions(SpineObject)

#The targets
###
include_directories(.)
file(GLOB source_files *.cpp *.h)
gd_add_clang_utils(SpineObject "${source_files}")
gd_add_extension_target(SpineObject "${source_files}")

#Linker files for the IDE extension
###
gd_extension_link_libraries(SpineObject)
Loading

0 comments on commit d0005ba

Please sign in to comment.