diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a4c781d9..c14f8ca03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -240,7 +240,8 @@ * Removed dependency on `RHI`, `RenderCore`, and `ShaderCore`. * Re-importing atlases and their textures now works consistently in all situations. * Added mix-and-match example to demonstrate the new Skin API. -* Materials on `SkeletonRendererComponent` are now blueprint read and writeable. This allows setting dynamic material instances at runtime +* Materials on `SkeletonRendererComponent` are now blueprint read and writeable. This allows setting dynamic material instances at runtime. +* Added `InitialSkin` property to `USpineWidget`. This allows previewing different skins in the UMG Designer. Initial skins can still be overridden via blueprint events such as `On Initialized`. ## C# ## * **Breaking changes** diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp index 5ba11f662..f26398a77 100644 --- a/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp +++ b/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp @@ -281,8 +281,16 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons } /* Skins. */ - for (size_t i = 0, n = (size_t)readVarint(input, true); i < n; ++i) - skeletonData->_skins.add(readSkin(input, false, skeletonData, nonessential)); + for (size_t i = 0, n = (size_t)readVarint(input, true); i < n; ++i) { + Skin* skin = readSkin(input, false, skeletonData, nonessential); + if (skin) + skeletonData->_skins.add(skin); + else { + delete input; + delete skeletonData; + return NULL; + } + } /* Linked meshes. */ for (int i = 0, n = _linkedMeshes.size(); i < n; ++i) { @@ -470,7 +478,12 @@ Skin *SkeletonBinary::readSkin(DataInput *input, bool defaultSkin, SkeletonData for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { String name(readStringRef(input, skeletonData)); Attachment *attachment = readAttachment(input, skin, slotIndex, name, skeletonData, nonessential); - if (attachment) skin->setAttachment(slotIndex, String(name), attachment); + if (attachment) + skin->setAttachment(slotIndex, String(name), attachment); + else { + delete skin; + return nullptr; + } } } return skin; @@ -488,6 +501,10 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo String path(readStringRef(input, skeletonData)); if (path.isEmpty()) path = name; RegionAttachment *region = _attachmentLoader->newRegionAttachment(*skin, String(name), String(path)); + if (!region) { + setError("Error reading attachment: ", name.buffer()); + return nullptr; + } region->_path = path; region->_rotation = readFloat(input); region->_x = readFloat(input) * _scale; @@ -504,6 +521,10 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo case AttachmentType_Boundingbox: { int vertexCount = readVarint(input, true); BoundingBoxAttachment *box = _attachmentLoader->newBoundingBoxAttachment(*skin, String(name)); + if (!box) { + setError("Error reading attachment: ", name.buffer()); + return nullptr; + } readVertices(input, static_cast(box), vertexCount); if (nonessential) { /* Skip color. */ @@ -519,6 +540,10 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo if (path.isEmpty()) path = name; mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path)); + if (!mesh) { + setError("Error reading attachment: ", name.buffer()); + return nullptr; + } mesh->_path = path; readColor(input, mesh->getColor()); vertexCount = readVarint(input, true); @@ -543,6 +568,10 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo if (path.isEmpty()) path = name; MeshAttachment *mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path)); + if (!mesh) { + setError("Error reading attachment: ", name.buffer()); + return nullptr; + } mesh->_path = path; readColor(input, mesh->getColor()); String skinName(readStringRef(input, skeletonData)); @@ -559,7 +588,11 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo return mesh; } case AttachmentType_Path: { - PathAttachment *path = _attachmentLoader->newPathAttachment(*skin, String(name)); + PathAttachment* path = _attachmentLoader->newPathAttachment(*skin, String(name)); + if (!path) { + setError("Error reading attachment: ", name.buffer()); + return nullptr; + } path->_closed = readBoolean(input); path->_constantSpeed = readBoolean(input); int vertexCount = readVarint(input, true); @@ -577,7 +610,11 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo return path; } case AttachmentType_Point: { - PointAttachment *point = _attachmentLoader->newPointAttachment(*skin, String(name)); + PointAttachment* point = _attachmentLoader->newPointAttachment(*skin, String(name)); + if (!point) { + setError("Error reading attachment: ", name.buffer()); + return nullptr; + } point->_rotation = readFloat(input); point->_x = readFloat(input) * _scale; point->_y = readFloat(input) * _scale; @@ -592,7 +629,11 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo case AttachmentType_Clipping: { int endSlotIndex = readVarint(input, true); int vertexCount = readVarint(input, true); - ClippingAttachment *clip = _attachmentLoader->newClippingAttachment(*skin, name); + ClippingAttachment* clip = _attachmentLoader->newClippingAttachment(*skin, name); + if (!clip) { + setError("Error reading attachment: ", name.buffer()); + return nullptr; + } readVertices(input, static_cast(clip), vertexCount); clip->_endSlot = skeletonData->_slots[endSlotIndex]; if (nonessential) { @@ -603,7 +644,7 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo return clip; } } - return NULL; + return nullptr; } void SkeletonBinary::readVertices(DataInput *input, VertexAttachment *attachment, int vertexCount) { diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineWidget.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineWidget.cpp index dc4ad0a4c..a39341da9 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineWidget.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineWidget.cpp @@ -99,6 +99,15 @@ void USpineWidget::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 { @@ -204,6 +213,7 @@ bool USpineWidget::SetSkin(const FString skinName) { spine::Skin* skin = skeleton->getData()->findSkin(TCHAR_TO_UTF8(*skinName)); if (!skin) return false; skeleton->setSkin(skin); + bSkinInitialized = true; return true; } else return false; @@ -211,7 +221,7 @@ bool USpineWidget::SetSkin(const FString skinName) { bool USpineWidget::SetSkins(UPARAM(ref) TArray& SkinNames) { CheckState(); - if (skeleton) { + 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)); @@ -222,6 +232,7 @@ bool USpineWidget::SetSkins(UPARAM(ref) TArray& SkinNames) { newSkin->addSkin(skin); } skeleton->setSkin(newSkin); + bSkinInitialized = true; if (customSkin != nullptr) { delete customSkin; } diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineWidget.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineWidget.h index c0e93e4e8..1ea17de7c 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineWidget.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpineWidget.h @@ -51,6 +51,9 @@ public: virtual const FText GetPaletteCategory() override; #endif + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine) + FString InitialSkin; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spine) USpineAtlasAsset* Atlas; @@ -216,7 +219,7 @@ protected: virtual void CheckState(); virtual void DisposeState(); - TSharedPtr slateWidget; + TSharedPtr slateWidget; spine::Skeleton* skeleton; spine::AnimationState* state; @@ -254,4 +257,5 @@ private: /* If the animation should update automatically. */ UPROPERTY() bool bAutoPlaying; + bool bSkinInitialized = false; };