Merge branch '4.1-beta' into 4.1-beta-physics

This commit is contained in:
Nathan Sweet 2021-12-10 17:24:14 -04:00
commit 98ccc98435
82 changed files with 3434 additions and 543 deletions

View File

@ -5,6 +5,18 @@
### SFML ### SFML
## C++ ## C++
* **Additions**
* Support for sequences.
* **Breaking changes**
* `RegionAttachment` and `MeshAttachment` now contain a `TextureRegion*` instead of encoding region fields directly.
* `AttachmentLoader::newRegionAttachment()` and `AttachmentLoader::newMeshAttachment()` now take an additional `Sequence*` parameter.
* `MeshAttachment::updateUVs()` was renamed to `MeshAttachment::updateRegion()`.
* `RegionAttachment::updateOffset()` was renamed to `RegionAttachment::updateRegion()`, `RegionAttachment::setUVs()` was merged into `updateRegion()`.
* `Slot::getAttachmentTime()` and `Slot::setAttachmentTime()` have been removed.
* `VertexAttachment::getDeformAttachment()` was renamed to `VertexAttachment::getTimelineAttachment()`.
* `Skeleton::update()` has been removed.
* `Skeleton::getTime()` has been removed.
### Cocos2d-x ### Cocos2d-x

View File

@ -95,6 +95,11 @@ cp -f ../coin/export/coin-pro.skel "$ROOT/spine-sfml/c/data/"
cp -f ../coin/export/coin-pma.atlas "$ROOT/spine-sfml/c/data/" cp -f ../coin/export/coin-pma.atlas "$ROOT/spine-sfml/c/data/"
cp -f ../coin/export/coin-pma.png "$ROOT/spine-sfml/c/data/" cp -f ../coin/export/coin-pma.png "$ROOT/spine-sfml/c/data/"
cp -f ../dragon/export/dragon-ess.json "$ROOT/spine-sfml/c/data/"
cp -f ../dragon/export/dragon-ess.skel "$ROOT/spine-sfml/c/data/"
cp -f ../dragon/export/dragon-pma.atlas "$ROOT/spine-sfml/c/data/"
cp -f ../dragon/export/dragon-pma*.png "$ROOT/spine-sfml/c/data/"
cp -f ../goblins/export/goblins-pro.json "$ROOT/spine-sfml/c/data/" cp -f ../goblins/export/goblins-pro.json "$ROOT/spine-sfml/c/data/"
cp -f ../goblins/export/goblins-pro.skel "$ROOT/spine-sfml/c/data/" cp -f ../goblins/export/goblins-pro.skel "$ROOT/spine-sfml/c/data/"
cp -f ../goblins/export/goblins-pma.atlas "$ROOT/spine-sfml/c/data/" cp -f ../goblins/export/goblins-pma.atlas "$ROOT/spine-sfml/c/data/"
@ -142,6 +147,11 @@ cp -f ../coin/export/coin-pro.skel "$ROOT/spine-sfml/cpp/data/"
cp -f ../coin/export/coin-pma.atlas "$ROOT/spine-sfml/cpp/data/" cp -f ../coin/export/coin-pma.atlas "$ROOT/spine-sfml/cpp/data/"
cp -f ../coin/export/coin-pma.png "$ROOT/spine-sfml/cpp/data/" cp -f ../coin/export/coin-pma.png "$ROOT/spine-sfml/cpp/data/"
cp -f ../dragon/export/dragon-ess.json "$ROOT/spine-sfml/cpp/data/"
cp -f ../dragon/export/dragon-ess.skel "$ROOT/spine-sfml/cpp/data/"
cp -f ../dragon/export/dragon-pma.atlas "$ROOT/spine-sfml/cpp/data/"
cp -f ../dragon/export/dragon-pma*.png "$ROOT/spine-sfml/cpp/data/"
cp -f ../goblins/export/goblins-pro.json "$ROOT/spine-sfml/cpp/data/" cp -f ../goblins/export/goblins-pro.json "$ROOT/spine-sfml/cpp/data/"
cp -f ../goblins/export/goblins-pro.skel "$ROOT/spine-sfml/cpp/data/" cp -f ../goblins/export/goblins-pro.skel "$ROOT/spine-sfml/cpp/data/"
cp -f ../goblins/export/goblins-pma.atlas "$ROOT/spine-sfml/cpp/data/" cp -f ../goblins/export/goblins-pma.atlas "$ROOT/spine-sfml/cpp/data/"
@ -320,8 +330,16 @@ cp -f ../dragon/export/dragon-ess.json "$UNITY_TARGET_DIR/dragon.json"
cp -f ../dragon/export/dragon-pma.atlas "$UNITY_TARGET_DIR/dragon.atlas.txt" cp -f ../dragon/export/dragon-pma.atlas "$UNITY_TARGET_DIR/dragon.atlas.txt"
$sed -i "s/dragon-pma.png/dragon.png/g" "$UNITY_TARGET_DIR/dragon.atlas.txt" $sed -i "s/dragon-pma.png/dragon.png/g" "$UNITY_TARGET_DIR/dragon.atlas.txt"
$sed -i "s/dragon-pma_2.png/dragon2.png/g" "$UNITY_TARGET_DIR/dragon.atlas.txt" $sed -i "s/dragon-pma_2.png/dragon2.png/g" "$UNITY_TARGET_DIR/dragon.atlas.txt"
$sed -i "s/dragon-pma_3.png/dragon3.png/g" "$UNITY_TARGET_DIR/dragon.atlas.txt"
$sed -i "s/dragon-pma_4.png/dragon4.png/g" "$UNITY_TARGET_DIR/dragon.atlas.txt"
$sed -i "s/dragon-pma_5.png/dragon5.png/g" "$UNITY_TARGET_DIR/dragon.atlas.txt"
$sed -i "s/dragon-pma_6.png/dragon6.png/g" "$UNITY_TARGET_DIR/dragon.atlas.txt"
cp -f ../dragon/export/dragon-pma.png "$UNITY_TARGET_DIR/dragon.png" cp -f ../dragon/export/dragon-pma.png "$UNITY_TARGET_DIR/dragon.png"
cp -f ../dragon/export/dragon-pma_2.png "$UNITY_TARGET_DIR/dragon2.png" cp -f ../dragon/export/dragon-pma_2.png "$UNITY_TARGET_DIR/dragon2.png"
cp -f ../dragon/export/dragon-pma_3.png "$UNITY_TARGET_DIR/dragon3.png"
cp -f ../dragon/export/dragon-pma_4.png "$UNITY_TARGET_DIR/dragon4.png"
cp -f ../dragon/export/dragon-pma_5.png "$UNITY_TARGET_DIR/dragon5.png"
cp -f ../dragon/export/dragon-pma_6.png "$UNITY_TARGET_DIR/dragon6.png"
UNITY_TARGET_DIR="$ROOT/spine-unity/Assets/Spine Examples/Spine Skeletons/Goblins" UNITY_TARGET_DIR="$ROOT/spine-unity/Assets/Spine Examples/Spine Skeletons/Goblins"
cp -f ../goblins/export/goblins-pro.json "$UNITY_TARGET_DIR/goblins.json" cp -f ../goblins/export/goblins-pro.json "$UNITY_TARGET_DIR/goblins.json"

View File

@ -35,6 +35,7 @@
#include <spine/SpineObject.h> #include <spine/SpineObject.h>
#include <spine/SpineString.h> #include <spine/SpineString.h>
#include <spine/HasRendererObject.h> #include <spine/HasRendererObject.h>
#include "TextureRegion.h"
namespace spine { namespace spine {
enum Format { enum Format {
@ -83,16 +84,12 @@ namespace spine {
} }
}; };
class SP_API AtlasRegion : public SpineObject { class SP_API AtlasRegion : public TextureRegion {
public: public:
AtlasPage *page; AtlasPage *page;
String name; String name;
int x, y, width, height;
float u, v, u2, v2;
float offsetX, offsetY;
int originalWidth, originalHeight;
int index; int index;
int degrees; int x, y;
Vector<int> splits; Vector<int> splits;
Vector<int> pads; Vector<int> pads;
Vector <String> names; Vector <String> names;

View File

@ -48,9 +48,9 @@ namespace spine {
explicit AtlasAttachmentLoader(Atlas *atlas); explicit AtlasAttachmentLoader(Atlas *atlas);
virtual RegionAttachment *newRegionAttachment(Skin &skin, const String &name, const String &path); virtual RegionAttachment *newRegionAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence);
virtual MeshAttachment *newMeshAttachment(Skin &skin, const String &name, const String &path); virtual MeshAttachment *newMeshAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence);
virtual BoundingBoxAttachment *newBoundingBoxAttachment(Skin &skin, const String &name); virtual BoundingBoxAttachment *newBoundingBoxAttachment(Skin &skin, const String &name);

View File

@ -51,6 +51,8 @@ namespace spine {
class ClippingAttachment; class ClippingAttachment;
class Sequence;
class SP_API AttachmentLoader : public SpineObject { class SP_API AttachmentLoader : public SpineObject {
public: public:
RTTI_DECL RTTI_DECL
@ -60,10 +62,10 @@ namespace spine {
virtual ~AttachmentLoader(); virtual ~AttachmentLoader();
/// @return May be NULL to not load any attachment. /// @return May be NULL to not load any attachment.
virtual RegionAttachment *newRegionAttachment(Skin &skin, const String &name, const String &path) = 0; virtual RegionAttachment *newRegionAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence) = 0;
/// @return May be NULL to not load any attachment. /// @return May be NULL to not load any attachment.
virtual MeshAttachment *newMeshAttachment(Skin &skin, const String &name, const String &path) = 0; virtual MeshAttachment *newMeshAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence) = 0;
/// @return May be NULL to not load any attachment. /// @return May be NULL to not load any attachment.
virtual BoundingBoxAttachment *newBoundingBoxAttachment(Skin &skin, const String &name) = 0; virtual BoundingBoxAttachment *newBoundingBoxAttachment(Skin &skin, const String &name) = 0;

View File

@ -31,6 +31,8 @@
#define Spine_MeshAttachment_h #define Spine_MeshAttachment_h
#include <spine/VertexAttachment.h> #include <spine/VertexAttachment.h>
#include <spine/TextureRegion.h>
#include <spine/Sequence.h>
#include <spine/Vector.h> #include <spine/Vector.h>
#include <spine/Color.h> #include <spine/Color.h>
#include <spine/HasRendererObject.h> #include <spine/HasRendererObject.h>
@ -51,7 +53,10 @@ namespace spine {
virtual ~MeshAttachment(); virtual ~MeshAttachment();
void updateUVs(); virtual void computeWorldVertices(Slot &slot, size_t start, size_t count, float *worldVertices, size_t offset,
size_t stride = 2);
void updateRegion();
int getHullLength(); int getHullLength();
@ -59,7 +64,7 @@ namespace spine {
Vector<float> &getRegionUVs(); Vector<float> &getRegionUVs();
/// The UV pair for each vertex, normalized within the entire texture. See also MeshAttachment::updateUVs /// The UV pair for each vertex, normalized within the entire texture. See also MeshAttachment::updateRegion
Vector<float> &getUVs(); Vector<float> &getUVs();
Vector<unsigned short> &getTriangles(); Vector<unsigned short> &getTriangles();
@ -70,52 +75,13 @@ namespace spine {
void setPath(const String &inValue); void setPath(const String &inValue);
float getRegionU(); TextureRegion *getRegion();
void setRegionU(float inValue); void setRegion(TextureRegion *region);
float getRegionV(); Sequence *getSequence();
void setRegionV(float inValue); void setSequence(Sequence *sequence);
float getRegionU2();
void setRegionU2(float inValue);
float getRegionV2();
void setRegionV2(float inValue);
int getRegionDegrees();
void setRegionDegrees(int inValue);
float getRegionOffsetX();
void setRegionOffsetX(float inValue);
// Pixels stripped from the bottom left, unrotated.
float getRegionOffsetY();
void setRegionOffsetY(float inValue);
float getRegionWidth();
void setRegionWidth(float inValue);
// Unrotated, stripped size.
float getRegionHeight();
void setRegionHeight(float inValue);
float getRegionOriginalWidth();
void setRegionOriginalWidth(float inValue);
// Unrotated, unstripped size.
float getRegionOriginalHeight();
void setRegionOriginalHeight(float inValue);
MeshAttachment *getParentMesh(); MeshAttachment *getParentMesh();
@ -137,22 +103,17 @@ namespace spine {
MeshAttachment *newLinkedMesh(); MeshAttachment *newLinkedMesh();
private: private:
float _regionOffsetX, _regionOffsetY, _regionWidth, _regionHeight, _regionOriginalWidth, _regionOriginalHeight;
MeshAttachment *_parentMesh; MeshAttachment *_parentMesh;
Vector<float> _uvs; Vector<float> _uvs;
Vector<float> _regionUVs; Vector<float> _regionUVs;
Vector<unsigned short> _triangles; Vector<unsigned short> _triangles;
Vector<unsigned short> _edges; Vector<unsigned short> _edges;
String _path; String _path;
float _regionU;
float _regionV;
float _regionU2;
float _regionV2;
float _width;
float _height;
Color _color; Color _color;
int _hullLength; int _hullLength;
int _regionDegrees; int _width, _height;
TextureRegion *_region;
Sequence *_sequence;
}; };
} }

View File

@ -33,6 +33,8 @@
#include <spine/Attachment.h> #include <spine/Attachment.h>
#include <spine/Vector.h> #include <spine/Vector.h>
#include <spine/Color.h> #include <spine/Color.h>
#include <spine/Sequence.h>
#include <spine/TextureRegion.h>
#include <spine/HasRendererObject.h> #include <spine/HasRendererObject.h>
@ -54,7 +56,9 @@ namespace spine {
public: public:
explicit RegionAttachment(const String &name); explicit RegionAttachment(const String &name);
void updateOffset(); virtual ~RegionAttachment();
void updateRegion();
void setUVs(float u, float v, float u2, float v2, float degrees); void setUVs(float u, float v, float u2, float v2, float degrees);
@ -63,9 +67,9 @@ namespace spine {
/// @param worldVertices The output world vertices. Must have a length greater than or equal to offset + 8. /// @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 offset The worldVertices index to begin writing values.
/// @param stride The number of worldVertices entries between the value pairs written. /// @param stride The number of worldVertices entries between the value pairs written.
void computeWorldVertices(Bone &bone, float *worldVertices, size_t offset, size_t stride = 2); void computeWorldVertices(Slot &slot, float *worldVertices, size_t offset, size_t stride = 2);
void computeWorldVertices(Bone &bone, Vector<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(); float getX();
@ -101,29 +105,13 @@ namespace spine {
void setPath(const String &inValue); void setPath(const String &inValue);
float getRegionOffsetX(); TextureRegion *getRegion();
void setRegionOffsetX(float inValue); void setRegion(TextureRegion *region);
float getRegionOffsetY(); Sequence *getSequence();
void setRegionOffsetY(float inValue); void setSequence(Sequence *sequence);
float getRegionWidth();
void setRegionWidth(float inValue);
float getRegionHeight();
void setRegionHeight(float inValue);
float getRegionOriginalWidth();
void setRegionOriginalWidth(float inValue);
float getRegionOriginalHeight();
void setRegionOriginalHeight(float inValue);
Vector<float> &getOffset(); Vector<float> &getOffset();
@ -142,15 +130,12 @@ namespace spine {
static const int BRY; static const int BRY;
float _x, _y, _rotation, _scaleX, _scaleY, _width, _height; float _x, _y, _rotation, _scaleX, _scaleY, _width, _height;
float _regionOffsetX, _regionOffsetY, _regionWidth, _regionHeight, _regionOriginalWidth, _regionOriginalHeight;
Vector<float> _vertexOffset; Vector<float> _vertexOffset;
Vector<float> _uvs; Vector<float> _uvs;
String _path; String _path;
float _regionU;
float _regionV;
float _regionU2;
float _regionV2;
Color _color; Color _color;
TextureRegion *_region;
Sequence *_sequence;
}; };
} }

View File

@ -0,0 +1,96 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, 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 SP_API Sequence : public SpineObject {
public:
explicit Sequence();
Sequence(int id, const Vector<TextureRegion *> &regions, int start, int digits, int setupIndex);
~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

@ -172,8 +172,6 @@ namespace spine {
/// @return May be NULL. /// @return May be NULL.
PathConstraint *findPathConstraint(const String &constraintName); PathConstraint *findPathConstraint(const String &constraintName);
void update(float delta);
/// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose. /// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.
/// @param outX The horizontal distance between the skeleton origin and the left side of the AABB. /// @param outX The horizontal distance between the skeleton origin and the left side of the AABB.
/// @param outY The vertical distance between the skeleton origin and the bottom side of the AABB. /// @param outY The vertical distance between the skeleton origin and the bottom side of the AABB.
@ -204,10 +202,6 @@ namespace spine {
Color &getColor(); Color &getColor();
float getTime();
void setTime(float inValue);
void setPosition(float x, float y); void setPosition(float x, float y);
float getX(); float getX();
@ -237,7 +231,6 @@ namespace spine {
Vector<Updatable *> _updateCache; Vector<Updatable *> _updateCache;
Skin *_skin; Skin *_skin;
Color _color; Color _color;
float _time;
float _scaleX, _scaleY; float _scaleX, _scaleY;
float _x, _y; float _x, _y;

View File

@ -114,12 +114,12 @@ namespace spine {
void setAttachmentState(int state); void setAttachmentState(int state);
float getAttachmentTime();
void setAttachmentTime(float inValue);
Vector<float> &getDeform(); Vector<float> &getDeform();
int getSequenceIndex();
void setSequenceIndex(int index);
private: private:
SlotData &_data; SlotData &_data;
Bone &_bone; Bone &_bone;
@ -129,7 +129,7 @@ namespace spine {
bool _hasDarkColor; bool _hasDarkColor;
Attachment *_attachment; Attachment *_attachment;
int _attachmentState; int _attachmentState;
float _attachmentTime; int _sequenceIndex;
Vector<float> _deform; Vector<float> _deform;
}; };
} }

View File

@ -0,0 +1,50 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, 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_TextureRegion_h
#define Spine_TextureRegion_h
#include <spine/Vector.h>
namespace spine {
class SP_API TextureRegion : public SpineObject {
public:
void *rendererObject;
float u, v, u2, v2;
int degrees;
float offsetX, offsetY;
int width, height;
int originalWidth, originalHeight;
TextureRegion(): rendererObject(NULL), u(0), v(0), u2(0), v2(0), degrees(0), offsetX(0), offsetY(0), width(0), height(0), originalWidth(0), originalHeight(0) {};
~TextureRegion() {};
};
}
#endif

View File

@ -62,10 +62,10 @@ namespace spine {
/// @param worldVertices The output world vertices. Must have a length greater than or equal to offset + count. /// @param worldVertices The output world vertices. Must have a length greater than or equal to offset + count.
/// @param offset The worldVertices index to begin writing values. /// @param offset The worldVertices index to begin writing values.
/// @param stride The number of worldVertices entries between the value pairs written. /// @param stride The number of worldVertices entries between the value pairs written.
void computeWorldVertices(Slot &slot, size_t start, size_t count, float *worldVertices, size_t offset, virtual void computeWorldVertices(Slot &slot, size_t start, size_t count, float *worldVertices, size_t offset,
size_t stride = 2); size_t stride = 2);
void computeWorldVertices(Slot &slot, size_t start, size_t count, Vector<float> &worldVertices, size_t offset, virtual void computeWorldVertices(Slot &slot, size_t start, size_t count, Vector<float> &worldVertices, size_t offset,
size_t stride = 2); size_t stride = 2);
/// Gets a unique ID for this attachment. /// Gets a unique ID for this attachment.
@ -79,9 +79,9 @@ namespace spine {
void setWorldVerticesLength(size_t inValue); void setWorldVerticesLength(size_t inValue);
VertexAttachment *getDeformAttachment(); Attachment * getTimelineAttachment();
void setDeformAttachment(VertexAttachment *attachment); void setTimelineAttachment(Attachment *attachment);
void copyTo(VertexAttachment *other); void copyTo(VertexAttachment *other);
@ -89,7 +89,7 @@ namespace spine {
Vector <size_t> _bones; Vector <size_t> _bones;
Vector<float> _vertices; Vector<float> _vertices;
size_t _worldVerticesLength; size_t _worldVerticesLength;
VertexAttachment *_deformAttachment; Attachment *_timelineAttachment;
private: private:
const int _id; const int _id;

View File

@ -48,53 +48,44 @@ namespace spine {
AtlasAttachmentLoader::AtlasAttachmentLoader(Atlas *atlas) : AttachmentLoader(), _atlas(atlas) { AtlasAttachmentLoader::AtlasAttachmentLoader(Atlas *atlas) : AttachmentLoader(), _atlas(atlas) {
} }
RegionAttachment *AtlasAttachmentLoader::newRegionAttachment(Skin &skin, const String &name, const String &path) { bool loadSequence (Atlas *atlas, const String &basePath, Sequence *sequence) {
SP_UNUSED(skin); Vector<TextureRegion *> &regions = sequence->getRegions();
for (int i = 0, n = regions.size(); i < n; i++) {
AtlasRegion *regionP = findRegion(path); String path = sequence->getPath(basePath, i);
if (!regionP) return NULL; regions[i] = atlas->findRegion(path);
if (!regions[i]) return false;
AtlasRegion &region = *regionP; regions[i]->rendererObject = regions[i];
}
RegionAttachment *attachmentP = new (__FILE__, __LINE__) RegionAttachment(name); return true;
RegionAttachment &attachment = *attachmentP;
attachment.setRendererObject(regionP);
attachment.setUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment._regionOffsetX = region.offsetX;
attachment._regionOffsetY = region.offsetY;
attachment._regionWidth = (float) region.width;
attachment._regionHeight = (float) region.height;
attachment._regionOriginalWidth = (float) region.originalWidth;
attachment._regionOriginalHeight = (float) region.originalHeight;
return attachmentP;
} }
MeshAttachment *AtlasAttachmentLoader::newMeshAttachment(Skin &skin, const String &name, const String &path) { RegionAttachment *AtlasAttachmentLoader::newRegionAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence) {
SP_UNUSED(skin); SP_UNUSED(skin);
RegionAttachment *attachment = new(__FILE__, __LINE__) RegionAttachment(name);
if (sequence) {
if (!loadSequence(_atlas, path, sequence)) return NULL;
} else {
AtlasRegion *region = findRegion(path);
if (!region) return NULL;
attachment->setRendererObject(region);
attachment->setRegion(region);
}
return attachment;
}
AtlasRegion *regionP = findRegion(path); MeshAttachment *AtlasAttachmentLoader::newMeshAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence) {
if (!regionP) return NULL; SP_UNUSED(skin);
MeshAttachment *attachment = new (__FILE__, __LINE__) MeshAttachment(name);
AtlasRegion &region = *regionP; if (sequence) {
if (!loadSequence(_atlas, path, sequence)) return NULL;
MeshAttachment *attachmentP = new (__FILE__, __LINE__) MeshAttachment(name); } else {
AtlasRegion *region = findRegion(path);
MeshAttachment &attachment = *attachmentP; if (!region) return NULL;
attachment.setRendererObject(regionP); attachment->setRendererObject(region);
attachment._regionU = region.u; attachment->setRegion(region);
attachment._regionV = region.v; }
attachment._regionU2 = region.u2; return attachment;
attachment._regionV2 = region.v2;
attachment._regionDegrees = region.degrees;
attachment._regionOffsetX = region.offsetX;
attachment._regionOffsetY = region.offsetY;
attachment._regionWidth = (float) region.width;
attachment._regionHeight = (float) region.height;
attachment._regionOriginalWidth = (float) region.originalWidth;
attachment._regionOriginalHeight = (float) region.originalHeight;
return attachmentP;
} }
BoundingBoxAttachment *AtlasAttachmentLoader::newBoundingBoxAttachment(Skin &skin, const String &name) { BoundingBoxAttachment *AtlasAttachmentLoader::newBoundingBoxAttachment(Skin &skin, const String &name) {

View File

@ -76,7 +76,7 @@ void DeformTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
} }
VertexAttachment *attachment = static_cast<VertexAttachment *>(slotAttachment); VertexAttachment *attachment = static_cast<VertexAttachment *>(slotAttachment);
if (attachment->_deformAttachment != _attachment) { if (attachment->_timelineAttachment != _attachment) {
return; return;
} }

View File

@ -39,43 +39,36 @@ using namespace spine;
RTTI_IMPL(MeshAttachment, VertexAttachment) RTTI_IMPL(MeshAttachment, VertexAttachment)
MeshAttachment::MeshAttachment(const String &name) : VertexAttachment(name), HasRendererObject(), MeshAttachment::MeshAttachment(const String &name) : VertexAttachment(name), HasRendererObject(),
_regionOffsetX(0),
_regionOffsetY(0),
_regionWidth(0),
_regionHeight(0),
_regionOriginalWidth(0),
_regionOriginalHeight(0),
_parentMesh(NULL), _parentMesh(NULL),
_path(), _path(),
_regionU(0),
_regionV(0),
_regionU2(0),
_regionV2(0),
_width(0),
_height(0),
_color(1, 1, 1, 1), _color(1, 1, 1, 1),
_hullLength(0), _hullLength(0),
_regionDegrees(0) {} _width(0),
_height(0),
_region(NULL),
_sequence(NULL) {}
MeshAttachment::~MeshAttachment() {} MeshAttachment::~MeshAttachment() {
if (_sequence) delete _sequence;
}
void MeshAttachment::updateUVs() { void MeshAttachment::updateRegion() {
if (_uvs.size() != _regionUVs.size()) { if (_uvs.size() != _regionUVs.size()) {
_uvs.setSize(_regionUVs.size(), 0); _uvs.setSize(_regionUVs.size(), 0);
} }
int i = 0, n = _regionUVs.size(); int i = 0, n = _regionUVs.size();
float u = _regionU, v = _regionV; float u = _region->u, v = _region->v;
float width = 0, height = 0; float width = 0, height = 0;
switch (_regionDegrees) { switch (_region->degrees) {
case 90: { case 90: {
float textureWidth = _regionHeight / (_regionU2 - _regionU); float textureWidth = _region->height / (_region->u2 - _region->u);
float textureHeight = _regionWidth / (_regionV2 - _regionV); float textureHeight = _region->width / (_region->v2 - _region->v);
u -= (_regionOriginalHeight - _regionOffsetY - _regionHeight) / textureWidth; u -= (_region->originalHeight - _region->offsetY - _region->height) / textureWidth;
v -= (_regionOriginalWidth - _regionOffsetX - _regionWidth) / textureHeight; v -= (_region->originalWidth - _region->offsetX - _region->width) / textureHeight;
width = _regionOriginalHeight / textureWidth; width = _region->originalHeight / textureWidth;
height = _regionOriginalWidth / textureHeight; height = _region->originalWidth / textureHeight;
for (i = 0; i < n; i += 2) { for (i = 0; i < n; i += 2) {
_uvs[i] = u + _regionUVs[i + 1] * width; _uvs[i] = u + _regionUVs[i + 1] * width;
_uvs[i + 1] = v + (1 - _regionUVs[i]) * height; _uvs[i + 1] = v + (1 - _regionUVs[i]) * height;
@ -83,12 +76,12 @@ void MeshAttachment::updateUVs() {
return; return;
} }
case 180: { case 180: {
float textureWidth = _regionWidth / (_regionU2 - _regionU); float textureWidth = _region->width / (_region->u2 - _region->u);
float textureHeight = _regionHeight / (_regionV2 - _regionV); float textureHeight = _region->height / (_region->v2 - _region->v);
u -= (_regionOriginalWidth - _regionOffsetX - _regionWidth) / textureWidth; u -= (_region->originalWidth - _region->offsetX - _region->width) / textureWidth;
v -= _regionOffsetY / textureHeight; v -= _region->offsetY / textureHeight;
width = _regionOriginalWidth / textureWidth; width = _region->originalWidth / textureWidth;
height = _regionOriginalHeight / textureHeight; height = _region->originalHeight / textureHeight;
for (i = 0; i < n; i += 2) { for (i = 0; i < n; i += 2) {
_uvs[i] = u + (1 - _regionUVs[i]) * width; _uvs[i] = u + (1 - _regionUVs[i]) * width;
_uvs[i + 1] = v + (1 - _regionUVs[i + 1]) * height; _uvs[i + 1] = v + (1 - _regionUVs[i + 1]) * height;
@ -96,12 +89,12 @@ void MeshAttachment::updateUVs() {
return; return;
} }
case 270: { case 270: {
float textureHeight = _regionHeight / (_regionV2 - _regionV); float textureHeight = _region->height / (_region->v2 - _region->v);
float textureWidth = _regionWidth / (_regionU2 - _regionU); float textureWidth = _region->width / (_region->u2 - _region->u);
u -= _regionOffsetY / textureWidth; u -= _region->offsetY / textureWidth;
v -= _regionOffsetX / textureHeight; v -= _region->offsetX / textureHeight;
width = _regionOriginalHeight / textureWidth; width = _region->originalHeight / textureWidth;
height = _regionOriginalWidth / textureHeight; height = _region->originalWidth / textureHeight;
for (i = 0; i < n; i += 2) { for (i = 0; i < n; i += 2) {
_uvs[i] = u + (1 - _regionUVs[i + 1]) * width; _uvs[i] = u + (1 - _regionUVs[i + 1]) * width;
_uvs[i + 1] = v + _regionUVs[i] * height; _uvs[i + 1] = v + _regionUVs[i] * height;
@ -109,12 +102,12 @@ void MeshAttachment::updateUVs() {
return; return;
} }
default: { default: {
float textureWidth = _regionWidth / (_regionU2 - _regionU); float textureWidth = _region->width / (_region->u2 - _region->u);
float textureHeight = _regionHeight / (_regionV2 - _regionV); float textureHeight = _region->height / (_region->v2 - _region->v);
u -= _regionOffsetX / textureWidth; u -= _region->offsetX / textureWidth;
v -= (_regionOriginalHeight - _regionOffsetY - _regionHeight) / textureHeight; v -= (_region->originalHeight - _region->offsetY - _region->height) / textureHeight;
width = _regionOriginalWidth / textureWidth; width = _region->originalWidth / textureWidth;
height = _regionOriginalHeight / textureHeight; height = _region->originalHeight / textureHeight;
for (i = 0; i < n; i += 2) { for (i = 0; i < n; i += 2) {
_uvs[i] = u + _regionUVs[i] * width; _uvs[i] = u + _regionUVs[i] * width;
_uvs[i + 1] = v + _regionUVs[i + 1] * height; _uvs[i + 1] = v + _regionUVs[i + 1] * height;
@ -151,94 +144,6 @@ void MeshAttachment::setPath(const String &inValue) {
_path = inValue; _path = inValue;
} }
float MeshAttachment::getRegionU() {
return _regionU;
}
void MeshAttachment::setRegionU(float inValue) {
_regionU = inValue;
}
float MeshAttachment::getRegionV() {
return _regionV;
}
void MeshAttachment::setRegionV(float inValue) {
_regionV = inValue;
}
float MeshAttachment::getRegionU2() {
return _regionU2;
}
void MeshAttachment::setRegionU2(float inValue) {
_regionU2 = inValue;
}
float MeshAttachment::getRegionV2() {
return _regionV2;
}
void MeshAttachment::setRegionV2(float inValue) {
_regionV2 = inValue;
}
int MeshAttachment::getRegionDegrees() {
return _regionDegrees;
}
void MeshAttachment::setRegionDegrees(int inValue) {
_regionDegrees = inValue;
}
float MeshAttachment::getRegionOffsetX() {
return _regionOffsetX;
}
void MeshAttachment::setRegionOffsetX(float inValue) {
_regionOffsetX = inValue;
}
float MeshAttachment::getRegionOffsetY() {
return _regionOffsetY;
}
void MeshAttachment::setRegionOffsetY(float inValue) {
_regionOffsetY = inValue;
}
float MeshAttachment::getRegionWidth() {
return _regionWidth;
}
void MeshAttachment::setRegionWidth(float inValue) {
_regionWidth = inValue;
}
float MeshAttachment::getRegionHeight() {
return _regionHeight;
}
void MeshAttachment::setRegionHeight(float inValue) {
_regionHeight = inValue;
}
float MeshAttachment::getRegionOriginalWidth() {
return _regionOriginalWidth;
}
void MeshAttachment::setRegionOriginalWidth(float inValue) {
_regionOriginalWidth = inValue;
}
float MeshAttachment::getRegionOriginalHeight() {
return _regionOriginalHeight;
}
void MeshAttachment::setRegionOriginalHeight(float inValue) {
_regionOriginalHeight = inValue;
}
MeshAttachment *MeshAttachment::getParentMesh() { MeshAttachment *MeshAttachment::getParentMesh() {
return _parentMesh; return _parentMesh;
} }
@ -287,17 +192,8 @@ Attachment *MeshAttachment::copy() {
MeshAttachment *copy = new (__FILE__, __LINE__) MeshAttachment(getName()); MeshAttachment *copy = new (__FILE__, __LINE__) MeshAttachment(getName());
copy->setRendererObject(getRendererObject()); copy->setRendererObject(getRendererObject());
copy->_regionU = _regionU; copy->setRegion(_region);
copy->_regionV = _regionV; copy->setSequence(_sequence != NULL ? _sequence->copy() : NULL);
copy->_regionU2 = _regionU2;
copy->_regionV2 = _regionV2;
copy->_regionDegrees = _regionDegrees;
copy->_regionOffsetX = _regionOffsetX;
copy->_regionOffsetY = _regionOffsetY;
copy->_regionWidth = _regionWidth;
copy->_regionHeight = _regionHeight;
copy->_regionOriginalWidth = _regionOriginalWidth;
copy->_regionOriginalHeight = _regionOriginalHeight;
copy->_path = _path; copy->_path = _path;
copy->_color.set(_color); copy->_color.set(_color);
@ -317,21 +213,17 @@ Attachment *MeshAttachment::copy() {
MeshAttachment *MeshAttachment::newLinkedMesh() { MeshAttachment *MeshAttachment::newLinkedMesh() {
MeshAttachment *copy = new (__FILE__, __LINE__) MeshAttachment(getName()); MeshAttachment *copy = new (__FILE__, __LINE__) MeshAttachment(getName());
copy->setRendererObject(getRendererObject()); copy->setRendererObject(getRendererObject());
copy->_regionU = _regionU; copy->setRegion(_region);
copy->_regionV = _regionV;
copy->_regionU2 = _regionU2;
copy->_regionV2 = _regionV2;
copy->_regionDegrees = _regionDegrees;
copy->_regionOffsetX = _regionOffsetX;
copy->_regionOffsetY = _regionOffsetY;
copy->_regionWidth = _regionWidth;
copy->_regionHeight = _regionHeight;
copy->_regionOriginalWidth = _regionOriginalWidth;
copy->_regionOriginalHeight = _regionOriginalHeight;
copy->_path = _path; copy->_path = _path;
copy->_color.set(_color); copy->_color.set(_color);
copy->_deformAttachment = this->_deformAttachment; copy->_timelineAttachment = this->_timelineAttachment;
copy->setParentMesh(_parentMesh ? _parentMesh : this); copy->setParentMesh(_parentMesh ? _parentMesh : this);
copy->updateUVs(); if (copy->_region) copy->updateRegion();
return copy; return copy;
} }
void MeshAttachment::computeWorldVertices(Slot &slot, size_t start, size_t count, float *worldVertices, size_t offset,
size_t stride) {
if (_sequence) _sequence->apply(&slot, this);
VertexAttachment::computeWorldVertices(slot, start, count, worldVertices, offset, stride);
}

View File

@ -34,6 +34,7 @@
#include <spine/RegionAttachment.h> #include <spine/RegionAttachment.h>
#include <spine/Bone.h> #include <spine/Bone.h>
#include <spine/Slot.h>
#include <assert.h> #include <assert.h>
@ -58,29 +59,25 @@ RegionAttachment::RegionAttachment(const String &name) : Attachment(name), HasRe
_scaleY(1), _scaleY(1),
_width(0), _width(0),
_height(0), _height(0),
_regionOffsetX(0),
_regionOffsetY(0),
_regionWidth(0),
_regionHeight(0),
_regionOriginalWidth(0),
_regionOriginalHeight(0),
_path(), _path(),
_regionU(0), _color(1, 1, 1, 1),
_regionV(0), _region(NULL),
_regionU2(0), _sequence(NULL) {
_regionV2(0),
_color(1, 1, 1, 1) {
_vertexOffset.setSize(NUM_UVS, 0); _vertexOffset.setSize(NUM_UVS, 0);
_uvs.setSize(NUM_UVS, 0); _uvs.setSize(NUM_UVS, 0);
} }
void RegionAttachment::updateOffset() { RegionAttachment::~RegionAttachment() {
float regionScaleX = _width / _regionOriginalWidth * _scaleX; if (_sequence) delete _sequence;
float regionScaleY = _height / _regionOriginalHeight * _scaleY; }
float localX = -_width / 2 * _scaleX + _regionOffsetX * regionScaleX;
float localY = -_height / 2 * _scaleY + _regionOffsetY * regionScaleY; void RegionAttachment::updateRegion() {
float localX2 = localX + _regionWidth * regionScaleX; float regionScaleX = _width / _region->originalWidth * _scaleX;
float localY2 = localY + _regionHeight * regionScaleY; float regionScaleY = _height / _region->originalHeight * _scaleY;
float localX = -_width / 2 * _scaleX + _region->offsetX * regionScaleX;
float localY = -_height / 2 * _scaleY + _region->offsetY * regionScaleY;
float localX2 = localX + _region->width * regionScaleX;
float localY2 = localY + _region->height * regionScaleY;
float cos = MathUtil::cosDeg(_rotation); float cos = MathUtil::cosDeg(_rotation);
float sin = MathUtil::sinDeg(_rotation); float sin = MathUtil::sinDeg(_rotation);
float localXCos = localX * cos + _x; float localXCos = localX * cos + _x;
@ -100,36 +97,37 @@ void RegionAttachment::updateOffset() {
_vertexOffset[URY] = localY2Cos + localX2Sin; _vertexOffset[URY] = localY2Cos + localX2Sin;
_vertexOffset[BRX] = localX2Cos - localYSin; _vertexOffset[BRX] = localX2Cos - localYSin;
_vertexOffset[BRY] = localYCos + localX2Sin; _vertexOffset[BRY] = localYCos + localX2Sin;
}
void RegionAttachment::setUVs(float u, float v, float u2, float v2, float degrees) { if (_region->degrees == 90) {
if (degrees == 90) { _uvs[URX] = _region->u;
_uvs[URX] = u; _uvs[URY] = _region->v2;
_uvs[URY] = v2; _uvs[BRX] = _region->u;
_uvs[BRX] = u; _uvs[BRY] = _region->v;
_uvs[BRY] = v; _uvs[BLX] = _region->u2;
_uvs[BLX] = u2; _uvs[BLY] = _region->v;
_uvs[BLY] = v; _uvs[ULX] = _region->u2;
_uvs[ULX] = u2; _uvs[ULY] = _region->v2;
_uvs[ULY] = v2;
} else { } else {
_uvs[ULX] = u; _uvs[ULX] = _region->u;
_uvs[ULY] = v2; _uvs[ULY] = _region->v2;
_uvs[URX] = u; _uvs[URX] = _region->u;
_uvs[URY] = v; _uvs[URY] = _region->v;
_uvs[BRX] = u2; _uvs[BRX] = _region->u2;
_uvs[BRY] = v; _uvs[BRY] = _region->v;
_uvs[BLX] = u2; _uvs[BLX] = _region->u2;
_uvs[BLY] = v2; _uvs[BLY] = _region->v2;
} }
} }
void RegionAttachment::computeWorldVertices(Bone &bone, Vector<float> &worldVertices, size_t offset, size_t stride) { void RegionAttachment::computeWorldVertices(Slot &slot, Vector<float> &worldVertices, size_t offset, size_t stride) {
assert(worldVertices.size() >= (offset + 8)); assert(worldVertices.size() >= (offset + 8));
computeWorldVertices(bone, worldVertices.buffer(), offset, stride); computeWorldVertices(slot, worldVertices.buffer(), offset, stride);
} }
void RegionAttachment::computeWorldVertices(Bone &bone, float *worldVertices, size_t offset, size_t stride) { void RegionAttachment::computeWorldVertices(Slot &slot, float *worldVertices, size_t offset, size_t stride) {
if (_sequence) _sequence->apply(&slot, this);
Bone &bone = slot.getBone();
float x = bone.getWorldX(), y = bone.getWorldY(); float x = bone.getWorldX(), y = bone.getWorldY();
float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD(); float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD();
float offsetX, offsetY; float offsetX, offsetY;
@ -222,52 +220,20 @@ void RegionAttachment::setPath(const String &inValue) {
_path = inValue; _path = inValue;
} }
float RegionAttachment::getRegionOffsetX() { TextureRegion *RegionAttachment::getRegion() {
return _regionOffsetX; return _region;
} }
void RegionAttachment::setRegionOffsetX(float inValue) { void RegionAttachment::setRegion(TextureRegion *region) {
_regionOffsetX = inValue; _region = region;
} }
float RegionAttachment::getRegionOffsetY() { Sequence *RegionAttachment::getSequence() {
return _regionOffsetY; return _sequence;
} }
void RegionAttachment::setRegionOffsetY(float inValue) { void RegionAttachment::setSequence(Sequence *sequence) {
_regionOffsetY = inValue; _sequence = sequence;
}
float RegionAttachment::getRegionWidth() {
return _regionWidth;
}
void RegionAttachment::setRegionWidth(float inValue) {
_regionWidth = inValue;
}
float RegionAttachment::getRegionHeight() {
return _regionHeight;
}
void RegionAttachment::setRegionHeight(float inValue) {
_regionHeight = inValue;
}
float RegionAttachment::getRegionOriginalWidth() {
return _regionOriginalWidth;
}
void RegionAttachment::setRegionOriginalWidth(float inValue) {
_regionOriginalWidth = inValue;
}
float RegionAttachment::getRegionOriginalHeight() {
return _regionOriginalHeight;
}
void RegionAttachment::setRegionOriginalHeight(float inValue) {
_regionOriginalHeight = inValue;
} }
Vector<float> &RegionAttachment::getOffset() { Vector<float> &RegionAttachment::getOffset() {
@ -284,12 +250,7 @@ spine::Color &RegionAttachment::getColor() {
Attachment *RegionAttachment::copy() { Attachment *RegionAttachment::copy() {
RegionAttachment *copy = new (__FILE__, __LINE__) RegionAttachment(getName()); RegionAttachment *copy = new (__FILE__, __LINE__) RegionAttachment(getName());
copy->_regionWidth = _regionWidth; copy->_region = _region;
copy->_regionHeight = _regionHeight;
copy->_regionOffsetX = _regionOffsetX;
copy->_regionOffsetY = _regionOffsetY;
copy->_regionOriginalWidth = _regionOriginalWidth;
copy->_regionOriginalHeight = _regionOriginalHeight;
copy->setRendererObject(getRendererObject()); copy->setRendererObject(getRendererObject());
copy->_path = _path; copy->_path = _path;
copy->_x = _x; copy->_x = _x;
@ -302,5 +263,6 @@ Attachment *RegionAttachment::copy() {
copy->_uvs.clearAndAddAll(_uvs); copy->_uvs.clearAndAddAll(_uvs);
copy->_vertexOffset.clearAndAddAll(_vertexOffset); copy->_vertexOffset.clearAndAddAll(_vertexOffset);
copy->_color.set(_color); copy->_color.set(_color);
copy->_sequence = _sequence != NULL ? _sequence->copy() : NULL;
return copy; return copy;
} }

View File

@ -0,0 +1,97 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, 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 <spine/Sequence.h>
#include <spine/Slot.h>
#include <spine/Attachment.h>
#include <spine/RegionAttachment.h>
#include <spine/MeshAttachment.h>
using namespace spine;
Sequence::Sequence() : _id(Sequence::getNextID()),
_regions(),
_start(0),
_digits(0),
_setupIndex(0) {
}
Sequence::~Sequence() {
}
Sequence *Sequence::copy() {
Sequence *copy = new (__FILE__, __LINE__) Sequence();
for (size_t i = 0; i < _regions.size(); i++) {
copy->_regions.add(_regions[i]);
}
copy->_start = _start;
copy->_digits = _digits;
copy->_setupIndex = _setupIndex;
return copy;
}
void Sequence::apply(Slot *slot, Attachment *attachment) {
int index = slot->getSequenceIndex();
if (index == -1) index = _setupIndex;
if (index >= (int)_regions.size()) index = _regions.size() - 1;
TextureRegion *region = _regions[index];
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
RegionAttachment *regionAttachment = static_cast<RegionAttachment *>(attachment);
if (regionAttachment->getRegion() != region) {
regionAttachment->setRegion(region);
regionAttachment->updateRegion();
}
}
if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
MeshAttachment *meshAttachment = static_cast<MeshAttachment *>(attachment);
if (meshAttachment->getRegion() != region) {
meshAttachment->setRegion(region);
meshAttachment->updateRegion();
}
}
}
String Sequence::getPath(const String &basePath, int index) {
String result(basePath);
String frame;
frame.append(_start + index);
for (int i = _digits - frame.length(); i > 0; i--)
result.append("0");
result.append(frame);
return result;
}
int Sequence::getNextID() {
static int _nextID = 0;
return _nextID;
}

View File

@ -60,7 +60,6 @@ using namespace spine;
Skeleton::Skeleton(SkeletonData *skeletonData) : _data(skeletonData), Skeleton::Skeleton(SkeletonData *skeletonData) : _data(skeletonData),
_skin(NULL), _skin(NULL),
_color(1, 1, 1, 1), _color(1, 1, 1, 1),
_time(0),
_scaleX(1), _scaleX(1),
_scaleY(1), _scaleY(1),
_x(0), _x(0),
@ -427,10 +426,6 @@ PathConstraint *Skeleton::findPathConstraint(const String &constraintName) {
return NULL; return NULL;
} }
void Skeleton::update(float delta) {
_time += delta;
}
void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHeight, Vector<float> &outVertexBuffer) { void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHeight, Vector<float> &outVertexBuffer) {
float minX = FLT_MAX; float minX = FLT_MAX;
float minY = FLT_MAX; float minY = FLT_MAX;
@ -450,7 +445,7 @@ void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHe
if (outVertexBuffer.size() < 8) { if (outVertexBuffer.size() < 8) {
outVertexBuffer.setSize(8, 0); outVertexBuffer.setSize(8, 0);
} }
regionAttachment->computeWorldVertices(slot->getBone(), outVertexBuffer, 0); regionAttachment->computeWorldVertices(*slot, outVertexBuffer, 0);
} else if (attachment != NULL && attachment->getRTTI().instanceOf(MeshAttachment::rtti)) { } else if (attachment != NULL && attachment->getRTTI().instanceOf(MeshAttachment::rtti)) {
MeshAttachment *mesh = static_cast<MeshAttachment *>(attachment); MeshAttachment *mesh = static_cast<MeshAttachment *>(attachment);
@ -459,7 +454,7 @@ void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHe
outVertexBuffer.setSize(verticesLength, 0); outVertexBuffer.setSize(verticesLength, 0);
} }
mesh->computeWorldVertices(*slot, 0, verticesLength, outVertexBuffer, 0); mesh->computeWorldVertices(*slot, 0, verticesLength, outVertexBuffer.buffer(), 0);
} }
for (size_t ii = 0; ii < verticesLength; ii += 2) { for (size_t ii = 0; ii < verticesLength; ii += 2) {
@ -523,14 +518,6 @@ Color &Skeleton::getColor() {
return _color; return _color;
} }
float Skeleton::getTime() {
return _time;
}
void Skeleton::setTime(float inValue) {
_time = inValue;
}
void Skeleton::setPosition(float x, float y) { void Skeleton::setPosition(float x, float y) {
_x = x; _x = x;
_y = y; _y = y;

View File

@ -298,10 +298,10 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
setError("Parent mesh not found: ", linkedMesh->_parent.buffer()); setError("Parent mesh not found: ", linkedMesh->_parent.buffer());
return NULL; return NULL;
} }
linkedMesh->_mesh->_deformAttachment = linkedMesh->_inheritDeform ? static_cast<VertexAttachment *>(parent) linkedMesh->_mesh->_timelineAttachment = linkedMesh->_inheritDeform ? static_cast<VertexAttachment *>(parent)
: linkedMesh->_mesh; : linkedMesh->_mesh;
linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent)); linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent));
linkedMesh->_mesh->updateUVs(); linkedMesh->_mesh->updateRegion();
_attachmentLoader->configureAttachment(linkedMesh->_mesh); _attachmentLoader->configureAttachment(linkedMesh->_mesh);
} }
ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes);
@ -502,7 +502,7 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
region->_width = readFloat(input) * _scale; region->_width = readFloat(input) * _scale;
region->_height = readFloat(input) * _scale; region->_height = readFloat(input) * _scale;
readColor(input, region->getColor()); readColor(input, region->getColor());
region->updateOffset(); region->updateRegion();
_attachmentLoader->configureAttachment(region); _attachmentLoader->configureAttachment(region);
return region; return region;
} }
@ -537,7 +537,7 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
readFloatArray(input, vertexCount << 1, 1, mesh->getRegionUVs()); readFloatArray(input, vertexCount << 1, 1, mesh->getRegionUVs());
readShortArray(input, mesh->getTriangles()); readShortArray(input, mesh->getTriangles());
readVertices(input, static_cast<VertexAttachment *>(mesh), vertexCount); readVertices(input, static_cast<VertexAttachment *>(mesh), vertexCount);
mesh->updateUVs(); mesh->updateRegion();
mesh->_hullLength = readVarint(input, true) << 1; mesh->_hullLength = readVarint(input, true) << 1;
if (nonessential) { if (nonessential) {
readShortArray(input, mesh->getEdges()); readShortArray(input, mesh->getEdges());

View File

@ -569,7 +569,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
color = Json::getString(attachmentMap, "color", 0); color = Json::getString(attachmentMap, "color", 0);
if (color) toColor(region->getColor(), color, true); if (color) toColor(region->getColor(), color, true);
region->updateOffset(); region->updateRegion();
_attachmentLoader->configureAttachment(region); _attachmentLoader->configureAttachment(region);
break; break;
} }
@ -610,7 +610,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
readVertices(attachmentMap, mesh, verticesLength); readVertices(attachmentMap, mesh, verticesLength);
mesh->updateUVs(); mesh->updateRegion();
mesh->_hullLength = Json::getInt(attachmentMap, "hull", 0); mesh->_hullLength = Json::getInt(attachmentMap, "hull", 0);
@ -722,10 +722,10 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
setError(root, "Parent mesh not found: ", linkedMesh->_parent.buffer()); setError(root, "Parent mesh not found: ", linkedMesh->_parent.buffer());
return NULL; return NULL;
} }
linkedMesh->_mesh->_deformAttachment = linkedMesh->_inheritDeform ? static_cast<VertexAttachment *>(parent) linkedMesh->_mesh->_timelineAttachment = linkedMesh->_inheritDeform ? static_cast<VertexAttachment *>(parent)
: linkedMesh->_mesh; : linkedMesh->_mesh;
linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent)); linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent));
linkedMesh->_mesh->updateUVs(); linkedMesh->_mesh->updateRegion();
_attachmentLoader->configureAttachment(linkedMesh->_mesh); _attachmentLoader->configureAttachment(linkedMesh->_mesh);
} }
ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes);

View File

@ -49,7 +49,7 @@ Slot::Slot(SlotData &data, Bone &bone) : _data(data),
_hasDarkColor(data.hasDarkColor()), _hasDarkColor(data.hasDarkColor()),
_attachment(NULL), _attachment(NULL),
_attachmentState(0), _attachmentState(0),
_attachmentTime(0) { _sequenceIndex(0) {
setToSetupPose(); setToSetupPose();
} }
@ -103,12 +103,13 @@ void Slot::setAttachment(Attachment *inValue) {
!_attachment || !_attachment ||
!inValue->getRTTI().instanceOf(VertexAttachment::rtti) || !inValue->getRTTI().instanceOf(VertexAttachment::rtti) ||
!_attachment->getRTTI().instanceOf(VertexAttachment::rtti) || !_attachment->getRTTI().instanceOf(VertexAttachment::rtti) ||
static_cast<VertexAttachment *>(inValue)->getDeformAttachment() != static_cast<VertexAttachment *>(_attachment)->getDeformAttachment()) { static_cast<VertexAttachment *>(inValue)->getTimelineAttachment() !=
static_cast<VertexAttachment *>(_attachment)->getTimelineAttachment()) {
_deform.clear(); _deform.clear();
} }
_attachment = inValue; _attachment = inValue;
_attachmentTime = _skeleton.getTime(); _sequenceIndex = -1;
} }
int Slot::getAttachmentState() { int Slot::getAttachmentState() {
@ -119,14 +120,14 @@ void Slot::setAttachmentState(int state) {
_attachmentState = state; _attachmentState = state;
} }
float Slot::getAttachmentTime() {
return _skeleton.getTime() - _attachmentTime;
}
void Slot::setAttachmentTime(float inValue) {
_attachmentTime = _skeleton.getTime() - inValue;
}
Vector<float> &Slot::getDeform() { Vector<float> &Slot::getDeform() {
return _deform; return _deform;
} }
int Slot::getSequenceIndex() {
return _sequenceIndex;
}
void Slot::setSequenceIndex(int index) {
_sequenceIndex = index;
}

View File

@ -43,7 +43,7 @@ using namespace spine;
RTTI_IMPL(VertexAttachment, Attachment) RTTI_IMPL(VertexAttachment, Attachment)
VertexAttachment::VertexAttachment(const String &name) : Attachment(name), _worldVerticesLength(0), VertexAttachment::VertexAttachment(const String &name) : Attachment(name), _worldVerticesLength(0),
_deformAttachment(this), _id(getNextID()) { _timelineAttachment(this), _id(getNextID()) {
} }
VertexAttachment::~VertexAttachment() { VertexAttachment::~VertexAttachment() {
@ -150,12 +150,12 @@ void VertexAttachment::setWorldVerticesLength(size_t inValue) {
_worldVerticesLength = inValue; _worldVerticesLength = inValue;
} }
VertexAttachment *VertexAttachment::getDeformAttachment() { Attachment * VertexAttachment::getTimelineAttachment() {
return _deformAttachment; return _timelineAttachment;
} }
void VertexAttachment::setDeformAttachment(VertexAttachment *attachment) { void VertexAttachment::setTimelineAttachment(Attachment *attachment) {
_deformAttachment = attachment; _timelineAttachment = attachment;
} }
int VertexAttachment::getNextID() { int VertexAttachment::getNextID() {
@ -167,5 +167,5 @@ void VertexAttachment::copyTo(VertexAttachment *other) {
other->_bones.clearAndAddAll(this->_bones); other->_bones.clearAndAddAll(this->_bones);
other->_vertices.clearAndAddAll(this->_vertices); other->_vertices.clearAndAddAll(this->_vertices);
other->_worldVerticesLength = this->_worldVerticesLength; other->_worldVerticesLength = this->_worldVerticesLength;
other->_deformAttachment = this->_deformAttachment; other->_timelineAttachment = this->_timelineAttachment;
} }

View File

@ -2348,6 +2348,30 @@ namespace Spine {
} }
float rotate, x, y, scaleX, scaleY, shearY; float rotate, x, y, scaleX, scaleY, shearY;
GetCurveValue(out rotate, out x, out y, out scaleX, out scaleY, out shearY, time);
if (blend == MixBlend.Setup) {
TransformConstraintData data = constraint.data;
constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
constraint.mixX = data.mixX + (x - data.mixX) * alpha;
constraint.mixY = data.mixY + (y - data.mixY) * alpha;
constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
} else {
constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
constraint.mixX += (x - constraint.mixX) * alpha;
constraint.mixY += (y - constraint.mixY) * alpha;
constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
}
}
public void GetCurveValue (out float rotate, out float x, out float y,
out float scaleX, out float scaleY, out float shearY, float time) {
float[] frames = this.frames;
int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES]; int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
switch (curveType) { switch (curveType) {
case LINEAR: case LINEAR:
@ -2383,23 +2407,6 @@ namespace Spine {
shearY = GetBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER); shearY = GetBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
break; break;
} }
if (blend == MixBlend.Setup) {
TransformConstraintData data = constraint.data;
constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
constraint.mixX = data.mixX + (x - data.mixX) * alpha;
constraint.mixY = data.mixY + (y - data.mixY) * alpha;
constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
} else {
constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
constraint.mixX += (x - constraint.mixX) * alpha;
constraint.mixY += (y - constraint.mixY) * alpha;
constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
}
} }
} }

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: f5588a995395d7d428bb39ca0bfb7bd8 guid: 1a1ea245fb673db4ba7bda20b0ed06e7
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@ -1,5 +1,5 @@
{ {
"skeleton": { "hash": "hj8P+t8L2OIWCj7RHV1Nzql4Y5E", "spine": "3.8.95", "images": "", "audio": "" }, "skeleton": { "hash": "hj8P+t8L2OIWCj7RHV1Nzql4Y5E", "spine": "4.1.08", "images": "", "audio": "" },
"bones": [ "bones": [
{ "name": "root" } { "name": "root" }
], ],

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c75b8f7aac7429e4ea6031cf0903cea1
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
{
"name": "com.esotericsoftware.spine.spine-csharp-tests",
"displayName": "spine-csharp Runtime Tests",
"description": "This plugin provides tests for the spine-csharp core runtime.",
"version": "4.1.0",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",
"email": "contact@esotericsoftware.com",
"url": "http://esotericsoftware.com/"
},
"dependencies": {
"com.esotericsoftware.spine.spine-csharp": "4.1.0"
},
"repository": {
"type": "git",
"url": "git@github.com:EsotericSoftware/spine-runtimes.git"
},
"keywords": [
"spine",
"spine-csharp",
"runtimes",
"tests",
"2d",
"skeletal",
"animation"
],
"license": "SEE LICENSE IN LICENSE",
"bugs": {
"url": "https://github.com/EsotericSoftware/spine-runtimes/issues"
},
"homepage": "https://github.com/EsotericSoftware/spine-runtimes#readme"
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4b249db0e1f4b8d428a050fcc092099e
PackageManifestImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,4 @@
{
"name": "spine-csharp-tests",
"references": [ "spine-csharp" ]
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 55c3c2e44e0ce38458817aeb64482f91
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 65e2134dba09ce14b99b93184153067f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ba84adcd3d667744bdac5d92c451668
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,129 @@
dragon-pma.png
size: 1024, 1024
filter: Linear, Linear
pma: true
back
bounds: 564, 534, 190, 185
chest
bounds: 2, 645, 136, 122
chin
bounds: 140, 619, 214, 146
front-toe-a
bounds: 2, 862, 29, 50
rotate: 90
front-toe-b
bounds: 467, 835, 56, 57
rotate: 90
head
bounds: 756, 398, 296, 260
rotate: 90
left-front-leg
bounds: 599, 834, 84, 57
left-front-thigh
bounds: 782, 819, 84, 72
left-rear-leg
bounds: 356, 558, 206, 177
left-rear-thigh
bounds: 216, 767, 91, 149
rotate: 90
left-wing01
bounds: 2, 268, 264, 589
rotate: 90
left-wing02
bounds: 2, 2, 264, 589
rotate: 90
right-front-leg
bounds: 113, 769, 101, 89
right-front-thigh
bounds: 758, 709, 108, 108
right-rear-leg
bounds: 640, 721, 116, 100
right-rear-thigh
bounds: 367, 742, 91, 149
rotate: 90
right-rear-toe
bounds: 2, 781, 109, 77
tail01
bounds: 868, 696, 120, 153
rotate: 90
tail02
bounds: 518, 737, 95, 120
rotate: 90
tail03
bounds: 868, 818, 73, 92
rotate: 90
tail04
bounds: 526, 835, 56, 71
rotate: 90
tail05
bounds: 406, 839, 52, 59
rotate: 90
tail06
bounds: 685, 823, 95, 68
thiagobrayner
bounds: 54, 860, 350, 31
dragon-pma_2.png
size: 1024, 1024
filter: Linear, Linear
pma: true
left-wing03
bounds: 2, 534, 264, 589
rotate: 90
left-wing04
bounds: 2, 268, 264, 589
rotate: 90
left-wing05
bounds: 593, 209, 264, 589
left-wing06
bounds: 2, 2, 264, 589
rotate: 90
dragon-pma_3.png
size: 1024, 1024
filter: Linear, Linear
pma: true
left-wing07
bounds: 2, 694, 264, 589
rotate: 90
left-wing08
bounds: 2, 428, 264, 589
rotate: 90
left-wing09
bounds: 593, 369, 264, 589
right-wing01
bounds: 2, 2, 365, 643
rotate: 90
dragon-pma_4.png
size: 1024, 1024
filter: Linear, Linear
pma: true
right-wing02
bounds: 2, 369, 365, 643
right-wing03
bounds: 369, 369, 365, 643
right-wing04
bounds: 2, 2, 365, 643
rotate: 90
dragon-pma_5.png
size: 1024, 1024
filter: Linear, Linear
pma: true
right-wing05
bounds: 2, 369, 365, 643
right-wing06
bounds: 369, 369, 365, 643
right-wing07
bounds: 2, 2, 365, 643
rotate: 90
dragon-pma_6.png
size: 1024, 1024
filter: Linear, Linear
pma: true
right-wing08
bounds: 2, 2, 365, 643
right-wing09
bounds: 369, 2, 365, 643

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,129 @@
dragon-pma.png
size: 1024, 1024
filter: Linear, Linear
pma: true
back
bounds: 564, 534, 190, 185
chest
bounds: 2, 645, 136, 122
chin
bounds: 140, 619, 214, 146
front-toe-a
bounds: 2, 862, 29, 50
rotate: 90
front-toe-b
bounds: 467, 835, 56, 57
rotate: 90
head
bounds: 756, 398, 296, 260
rotate: 90
left-front-leg
bounds: 599, 834, 84, 57
left-front-thigh
bounds: 782, 819, 84, 72
left-rear-leg
bounds: 356, 558, 206, 177
left-rear-thigh
bounds: 216, 767, 91, 149
rotate: 90
left-wing01
bounds: 2, 268, 264, 589
rotate: 90
left-wing02
bounds: 2, 2, 264, 589
rotate: 90
right-front-leg
bounds: 113, 769, 101, 89
right-front-thigh
bounds: 758, 709, 108, 108
right-rear-leg
bounds: 640, 721, 116, 100
right-rear-thigh
bounds: 367, 742, 91, 149
rotate: 90
right-rear-toe
bounds: 2, 781, 109, 77
tail01
bounds: 868, 696, 120, 153
rotate: 90
tail02
bounds: 518, 737, 95, 120
rotate: 90
tail03
bounds: 868, 818, 73, 92
rotate: 90
tail04
bounds: 526, 835, 56, 71
rotate: 90
tail05
bounds: 406, 839, 52, 59
rotate: 90
tail06
bounds: 685, 823, 95, 68
thiagobrayner
bounds: 54, 860, 350, 31
dragon-pma_2.png
size: 1024, 1024
filter: Linear, Linear
pma: true
left-wing03
bounds: 2, 534, 264, 589
rotate: 90
left-wing04
bounds: 2, 268, 264, 589
rotate: 90
left-wing05
bounds: 593, 209, 264, 589
left-wing06
bounds: 2, 2, 264, 589
rotate: 90
dragon-pma_3.png
size: 1024, 1024
filter: Linear, Linear
pma: true
left-wing07
bounds: 2, 694, 264, 589
rotate: 90
left-wing08
bounds: 2, 428, 264, 589
rotate: 90
left-wing09
bounds: 593, 369, 264, 589
right-wing01
bounds: 2, 2, 365, 643
rotate: 90
dragon-pma_4.png
size: 1024, 1024
filter: Linear, Linear
pma: true
right-wing02
bounds: 2, 369, 365, 643
right-wing03
bounds: 369, 369, 365, 643
right-wing04
bounds: 2, 2, 365, 643
rotate: 90
dragon-pma_5.png
size: 1024, 1024
filter: Linear, Linear
pma: true
right-wing05
bounds: 2, 369, 365, 643
right-wing06
bounds: 369, 369, 365, 643
right-wing07
bounds: 2, 2, 365, 643
rotate: 90
dragon-pma_6.png
size: 1024, 1024
filter: Linear, Linear
pma: true
right-wing08
bounds: 2, 2, 365, 643
right-wing09
bounds: 369, 2, 365, 643

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -479,6 +479,40 @@ void coin(SkeletonData *skeletonData, Atlas *atlas) {
} }
} }
void dragon(SkeletonData *skeletonData, Atlas *atlas) {
SP_UNUSED(atlas);
SkeletonDrawable drawable(skeletonData);
drawable.timeScale = 1;
drawable.setUsePremultipliedAlpha(true);
Skeleton *skeleton = drawable.skeleton;
skeleton->setPosition(320, 320);
skeleton->updateWorldTransform();
drawable.state->setAnimation(0, "flying", true);
sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - dragon");
window.setFramerateLimit(60);
sf::Event event;
sf::Clock deltaClock;
while (window.isOpen()) {
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
float delta = deltaClock.getElapsedTime().asSeconds();
deltaClock.restart();
drawable.update(delta);
window.clear();
window.draw(drawable);
window.display();
}
}
void owl(SkeletonData *skeletonData, Atlas *atlas) { void owl(SkeletonData *skeletonData, Atlas *atlas) {
SP_UNUSED(atlas); SP_UNUSED(atlas);
@ -610,6 +644,7 @@ DebugExtension dbgExtension(SpineExtension::getInstance());
int main() { int main() {
SpineExtension::setInstance(&dbgExtension); SpineExtension::setInstance(&dbgExtension);
testcase(dragon, "data/dragon-ess.json", "data/dragon-ess.skel", "data/dragon-pma.atlas", 0.6f);
testcase(ikDemo, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f); testcase(ikDemo, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);
testcase(mixAndMatch, "data/mix-and-match-pro.json", "data/mix-and-match-pro.skel", "data/mix-and-match-pma.atlas", 0.5f); testcase(mixAndMatch, "data/mix-and-match-pro.json", "data/mix-and-match-pro.skel", "data/mix-and-match-pma.atlas", 0.5f);
testcase(coin, "data/coin-pro.json", "data/coin-pro.skel", "data/coin-pma.atlas", 0.5f); testcase(coin, "data/coin-pro.json", "data/coin-pro.skel", "data/coin-pma.atlas", 0.5f);

View File

@ -276,7 +276,7 @@ GameObject:
- component: {fileID: 651278530} - component: {fileID: 651278530}
- component: {fileID: 651278529} - component: {fileID: 651278529}
m_Layer: 0 m_Layer: 0
m_Name: 2 DataAssets from Exports m_Name: 2 RuntimeLoadFromExports
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0

View File

@ -32,13 +32,17 @@ using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace Spine.Unity.Examples { namespace Spine.Unity.Examples {
public class DataAssetsFromExportsExample : MonoBehaviour { public class RuntimeLoadFromExportsExample : MonoBehaviour {
public TextAsset skeletonJson; public TextAsset skeletonJson;
public TextAsset atlasText; public TextAsset atlasText;
public Texture2D[] textures; public Texture2D[] textures;
public Material materialPropertySource; public Material materialPropertySource;
public float delay = 0;
public string skinName;
public string animationName;
SpineAtlasAsset runtimeAtlasAsset; SpineAtlasAsset runtimeAtlasAsset;
SkeletonDataAsset runtimeSkeletonDataAsset; SkeletonDataAsset runtimeSkeletonDataAsset;
SkeletonAnimation runtimeSkeletonAnimation; SkeletonAnimation runtimeSkeletonAnimation;
@ -54,19 +58,19 @@ namespace Spine.Unity.Examples {
IEnumerator Start () { IEnumerator Start () {
CreateRuntimeAssetsAndGameObject(); CreateRuntimeAssetsAndGameObject();
runtimeSkeletonDataAsset.GetSkeletonData(false); // preload. if (delay > 0) {
yield return new WaitForSeconds(0.5f); runtimeSkeletonDataAsset.GetSkeletonData(false); // preload
yield return new WaitForSeconds(delay);
}
runtimeSkeletonAnimation = SkeletonAnimation.NewSkeletonAnimationGameObject(runtimeSkeletonDataAsset); runtimeSkeletonAnimation = SkeletonAnimation.NewSkeletonAnimationGameObject(runtimeSkeletonDataAsset);
// Extra Stuff // additional initialization
runtimeSkeletonAnimation.Initialize(false); runtimeSkeletonAnimation.Initialize(false);
runtimeSkeletonAnimation.Skeleton.SetSkin("base"); if (skinName != "")
runtimeSkeletonAnimation.Skeleton.SetSkin(skinName);
runtimeSkeletonAnimation.Skeleton.SetSlotsToSetupPose(); runtimeSkeletonAnimation.Skeleton.SetSlotsToSetupPose();
runtimeSkeletonAnimation.AnimationState.SetAnimation(0, "run", true); if (animationName != "")
runtimeSkeletonAnimation.GetComponent<MeshRenderer>().sortingOrder = 10; runtimeSkeletonAnimation.AnimationState.SetAnimation(0, animationName, true);
runtimeSkeletonAnimation.transform.Translate(Vector3.down * 2);
} }
} }

View File

@ -38,7 +38,6 @@ namespace Spine.Unity.Editor {
public class SkeletonAnimationInspector : SkeletonRendererInspector { public class SkeletonAnimationInspector : SkeletonRendererInspector {
protected SerializedProperty animationName, loop, timeScale, autoReset; protected SerializedProperty animationName, loop, timeScale, autoReset;
protected bool wasAnimationParameterChanged = false; protected bool wasAnimationParameterChanged = false;
protected bool requireRepaint;
readonly GUIContent LoopLabel = new GUIContent("Loop", "Whether or not .AnimationName should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected."); readonly GUIContent LoopLabel = new GUIContent("Loop", "Whether or not .AnimationName should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.");
readonly GUIContent TimeScaleLabel = new GUIContent("Time Scale", "The rate at which animations progress over time. 1 means normal speed. 0.5 means 50% speed."); readonly GUIContent TimeScaleLabel = new GUIContent("Time Scale", "The rate at which animations progress over time. 1 means normal speed. 0.5 means 50% speed.");
@ -79,13 +78,6 @@ namespace Spine.Unity.Editor {
SkeletonRootMotionParameter(); SkeletonRootMotionParameter();
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
if (!isInspectingPrefab) {
if (requireRepaint) {
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
requireRepaint = false;
}
}
} }
protected void TrySetAnimation (SkeletonAnimation skeletonAnimation) { protected void TrySetAnimation (SkeletonAnimation skeletonAnimation) {

View File

@ -70,6 +70,8 @@ namespace Spine.Unity.Editor {
protected SerializedProperty maskInteraction; protected SerializedProperty maskInteraction;
protected SerializedProperty maskMaterialsNone, maskMaterialsInside, maskMaterialsOutside; protected SerializedProperty maskMaterialsNone, maskMaterialsInside, maskMaterialsOutside;
protected SpineInspectorUtility.SerializedSortingProperties sortingProperties; protected SpineInspectorUtility.SerializedSortingProperties sortingProperties;
protected bool wasInitParameterChanged = false;
protected bool requireRepaint = false;
protected bool isInspectingPrefab; protected bool isInspectingPrefab;
protected bool forceReloadQueued = false; protected bool forceReloadQueued = false;
@ -196,6 +198,13 @@ namespace Spine.Unity.Editor {
SceneView.RepaintAll(); SceneView.RepaintAll();
} }
} }
if (!isInspectingPrefab) {
if (requireRepaint) {
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
requireRepaint = false;
}
}
} }
protected virtual void DrawInspectorGUI (bool multi) { protected virtual void DrawInspectorGUI (bool multi) {
@ -259,6 +268,9 @@ namespace Spine.Unity.Editor {
bool valid = TargetIsValid; bool valid = TargetIsValid;
foreach (var o in targets)
ApplyModifiedMeshParameters(o as SkeletonRenderer);
// Fields. // Fields.
if (multi) { if (multi) {
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) { using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
@ -330,8 +342,10 @@ namespace Spine.Unity.Editor {
using (new SpineInspectorUtility.IndentScope()) { using (new SpineInspectorUtility.IndentScope()) {
using (new EditorGUILayout.HorizontalScope()) { using (new EditorGUILayout.HorizontalScope()) {
EditorGUI.BeginChangeCheck();
SpineInspectorUtility.ToggleLeftLayout(initialFlipX); SpineInspectorUtility.ToggleLeftLayout(initialFlipX);
SpineInspectorUtility.ToggleLeftLayout(initialFlipY); SpineInspectorUtility.ToggleLeftLayout(initialFlipY);
wasInitParameterChanged |= EditorGUI.EndChangeCheck(); // Value used in the next update.
EditorGUILayout.Space(); EditorGUILayout.Space();
} }
@ -421,6 +435,23 @@ namespace Spine.Unity.Editor {
} }
} }
protected void ApplyModifiedMeshParameters (SkeletonRenderer skeletonRenderer) {
if (skeletonRenderer == null) return;
if (!skeletonRenderer.valid)
return;
if (!isInspectingPrefab) {
if (wasInitParameterChanged) {
wasInitParameterChanged = false;
if (!Application.isPlaying) {
skeletonRenderer.Initialize(true);
skeletonRenderer.LateUpdate();
requireRepaint = true;
}
}
}
}
protected void SkeletonRootMotionParameter () { protected void SkeletonRootMotionParameter () {
SkeletonRootMotionParameter(targets); SkeletonRootMotionParameter(targets);
} }

View File

@ -118,11 +118,12 @@ namespace Spine.Unity.Editor {
static readonly AttachmentType[] AtlasTypes = { AttachmentType.Region, AttachmentType.Linkedmesh, AttachmentType.Mesh }; static readonly AttachmentType[] AtlasTypes = { AttachmentType.Region, AttachmentType.Linkedmesh, AttachmentType.Mesh };
public static List<string> GetRequiredAtlasRegions (string skeletonDataPath) { public static List<string> GetRequiredAtlasRegions (string skeletonDataPath) {
List<string> requiredPaths = new List<string>(); HashSet<string> requiredPaths = new HashSet<string>();
if (skeletonDataPath.Contains(".skel")) { if (skeletonDataPath.Contains(".skel")) {
AddRequiredAtlasRegionsFromBinary(skeletonDataPath, requiredPaths); List<string> requiredPathsResult = new List<string>();
return requiredPaths; AddRequiredAtlasRegionsFromBinary(skeletonDataPath, requiredPathsResult);
return requiredPathsResult;
} }
TextReader reader = null; TextReader reader = null;
@ -143,11 +144,11 @@ namespace Spine.Unity.Editor {
} }
if (root == null || !root.ContainsKey("skins")) if (root == null || !root.ContainsKey("skins"))
return requiredPaths; return new List<string>();
var skinsList = root["skins"] as List<object>; var skinsList = root["skins"] as List<object>;
if (skinsList == null) if (skinsList == null)
return requiredPaths; return new List<string>();
foreach (Dictionary<string, object> skinMap in skinsList) { foreach (Dictionary<string, object> skinMap in skinsList) {
if (!skinMap.ContainsKey("attachments")) if (!skinMap.ContainsKey("attachments"))
@ -189,7 +190,7 @@ namespace Spine.Unity.Editor {
} }
} }
return requiredPaths; return requiredPaths.ToList();
} }
internal static void AddRequiredAtlasRegionsFromBinary (string skeletonDataPath, List<string> requiredPaths) { internal static void AddRequiredAtlasRegionsFromBinary (string skeletonDataPath, List<string> requiredPaths) {

View File

@ -96,8 +96,7 @@ namespace Spine.Unity {
if (hasBinaryExtension) { if (hasBinaryExtension) {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.skel.bytes' but content looks like a '.json' file.\n" problemDescription = string.Format("Failed to read '{0}'. Extension is '.skel.bytes' but content looks like a '.json' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name); + "Did you choose the wrong extension upon export?\n", asset.name);
} } else {
else {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.json' but content looks like binary 'skel.bytes' file.\n" problemDescription = string.Format("Failed to read '{0}'. Extension is '.json' but content looks like binary 'skel.bytes' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name); + "Did you choose the wrong extension upon export?\n", asset.name);
} }
@ -110,19 +109,16 @@ namespace Spine.Unity {
using (var memStream = new MemoryStream(asset.bytes)) { using (var memStream = new MemoryStream(asset.bytes)) {
fileVersion.rawVersion = SkeletonBinary.GetVersionString(memStream); fileVersion.rawVersion = SkeletonBinary.GetVersionString(memStream);
} }
} } catch (System.Exception e) {
catch (System.Exception e) {
problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e); problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false; isSpineSkeletonData = false;
return null; return null;
} }
} } else {
else {
Match match = jsonVersionRegex.Match(asset.text); Match match = jsonVersionRegex.Match(asset.text);
if (match != null) { if (match != null) {
fileVersion.rawVersion = match.Groups[1].Value; fileVersion.rawVersion = match.Groups[1].Value;
} } else {
else {
object obj = Json.Deserialize(new StringReader(asset.text)); object obj = Json.Deserialize(new StringReader(asset.text));
if (obj == null) { if (obj == null) {
problemDescription = string.Format("'{0}' is not valid JSON.", asset.name); problemDescription = string.Format("'{0}' is not valid JSON.", asset.name);
@ -156,8 +152,7 @@ namespace Spine.Unity {
try { try {
fileVersion.version = new[]{ int.Parse(versionSplit[0], CultureInfo.InvariantCulture), fileVersion.version = new[]{ int.Parse(versionSplit[0], CultureInfo.InvariantCulture),
int.Parse(versionSplit[1], CultureInfo.InvariantCulture) }; int.Parse(versionSplit[1], CultureInfo.InvariantCulture) };
} } catch (System.Exception e) {
catch (System.Exception e) {
problemDescription = string.Format("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e); problemDescription = string.Format("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false; isSpineSkeletonData = false;
return null; return null;
@ -179,9 +174,11 @@ namespace Spine.Unity {
if (char.IsWhiteSpace(c)) if (char.IsWhiteSpace(c))
continue; continue;
if (!openingBraceFound) { if (!openingBraceFound) {
if (c == '{') openingBraceFound = true; if (c == '{' || c == '[') openingBraceFound = true;
else return false; else return false;
} else } else if (c == '{' || c == '[' || c == ']' || c == '}' || c == ',')
continue;
else
return c == '"'; return c == '"';
} }
return true; return true;

View File

@ -66,6 +66,8 @@ namespace Spine.Unity {
protected ISkeletonComponent skeletonComponent; protected ISkeletonComponent skeletonComponent;
protected Bone rootMotionBone; protected Bone rootMotionBone;
protected int rootMotionBoneIndex; protected int rootMotionBoneIndex;
protected List<int> transformConstraintIndices = new List<int>();
protected List<Vector2> transformConstraintLastPos = new List<Vector2>();
protected List<Bone> topLevelBones = new List<Bone>(); protected List<Bone> topLevelBones = new List<Bone>();
protected Vector2 initialOffset = Vector2.zero; protected Vector2 initialOffset = Vector2.zero;
protected Vector2 tempSkeletonDisplacement; protected Vector2 tempSkeletonDisplacement;
@ -94,7 +96,6 @@ namespace Spine.Unity {
return; // Root motion is only applied when component is enabled. return; // Root motion is only applied when component is enabled.
if (rigidBody2D != null) { if (rigidBody2D != null) {
Vector2 gravityAndVelocityMovement = Vector2.zero; Vector2 gravityAndVelocityMovement = Vector2.zero;
if (applyRigidbody2DGravity) { if (applyRigidbody2DGravity) {
float deltaTime = Time.fixedDeltaTime; float deltaTime = Time.fixedDeltaTime;
@ -107,11 +108,11 @@ namespace Spine.Unity {
rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y) rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y)
+ rigidbodyDisplacement); + rigidbodyDisplacement);
} } else if (rigidBody != null) {
if (rigidBody != null) {
rigidBody.MovePosition(transform.position rigidBody.MovePosition(transform.position
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0)); + new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
} } else return;
Vector2 parentBoneScale; Vector2 parentBoneScale;
GetScaleAffectingRootMotion(out parentBoneScale); GetScaleAffectingRootMotion(out parentBoneScale);
ClearEffectiveBoneOffsets(parentBoneScale); ClearEffectiveBoneOffsets(parentBoneScale);
@ -155,6 +156,7 @@ namespace Spine.Unity {
if (bone != null) { if (bone != null) {
this.rootMotionBoneIndex = bone.Data.Index; this.rootMotionBoneIndex = bone.Data.Index;
this.rootMotionBone = bone; this.rootMotionBone = bone;
FindTransformConstraintsAffectingBone();
} else { } else {
Debug.Log("Bone named \"" + name + "\" could not be found."); Debug.Log("Bone named \"" + name + "\" could not be found.");
this.rootMotionBoneIndex = 0; this.rootMotionBoneIndex = 0;
@ -196,37 +198,132 @@ namespace Spine.Unity {
public Vector2 GetAnimationRootMotion (float startTime, float endTime, public Vector2 GetAnimationRootMotion (float startTime, float endTime,
Animation animation) { Animation animation) {
var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); if (startTime == endTime)
if (timeline != null) { return Vector2.zero;
return GetTimelineMovementDelta(startTime, endTime, timeline, animation);
TranslateTimeline translateTimeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
TranslateXTimeline xTimeline = animation.FindTimelineForBone<TranslateXTimeline>(rootMotionBoneIndex);
TranslateYTimeline yTimeline = animation.FindTimelineForBone<TranslateYTimeline>(rootMotionBoneIndex);
// Non-looped base
Vector2 endPos = Vector2.zero;
Vector2 startPos = Vector2.zero;
if (translateTimeline != null) {
endPos = translateTimeline.Evaluate(endTime);
startPos = translateTimeline.Evaluate(startTime);
} else if (xTimeline != null || yTimeline != null) {
endPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime);
startPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime);
}
var transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items;
foreach (int constraintIndex in this.transformConstraintIndices) {
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
ApplyConstraintToPos(animation, constraint, constraintIndex, endTime, false, ref endPos);
ApplyConstraintToPos(animation, constraint, constraintIndex, startTime, true, ref startPos);
}
Vector2 currentDelta = endPos - startPos;
// Looped additions
if (startTime > endTime) {
Vector2 loopPos = Vector2.zero;
Vector2 zeroPos = Vector2.zero;
if (translateTimeline != null) {
loopPos = translateTimeline.Evaluate(animation.Duration);
zeroPos = translateTimeline.Evaluate(0);
} else if (xTimeline != null || yTimeline != null) {
loopPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration);
zeroPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0);
}
foreach (int constraintIndex in this.transformConstraintIndices) {
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
ApplyConstraintToPos(animation, constraint, constraintIndex, animation.Duration, false, ref loopPos);
ApplyConstraintToPos(animation, constraint, constraintIndex, 0, false, ref zeroPos);
}
currentDelta += loopPos - zeroPos;
}
UpdateLastConstraintPos(transformConstraintsItems);
return currentDelta;
}
void ApplyConstraintToPos (Animation animation, TransformConstraint constraint,
int constraintIndex, float time, bool useLastConstraintPos, ref Vector2 pos) {
TransformConstraintTimeline timeline = animation.FindTransformConstraintTimeline(constraintIndex);
if (timeline == null)
return;
Vector2 mixXY = timeline.EvaluateTranslateXYMix(time);
Vector2 invMixXY = timeline.EvaluateTranslateXYMix(time);
Vector2 constraintPos;
if (useLastConstraintPos)
constraintPos = transformConstraintLastPos[constraintIndex];
else {
Bone targetBone = constraint.Target;
constraintPos = new Vector2(targetBone.X, targetBone.Y);
}
pos = new Vector2(
pos.x * invMixXY.x + constraintPos.x * mixXY.x,
pos.y * invMixXY.y + constraintPos.y * mixXY.y);
}
void UpdateLastConstraintPos (TransformConstraint[] transformConstraintsItems) {
foreach (int constraintIndex in this.transformConstraintIndices) {
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
Bone targetBone = constraint.Target;
transformConstraintLastPos[constraintIndex] = new Vector2(targetBone.X, targetBone.Y);
} }
return Vector2.zero;
} }
public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) { public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) {
RootMotionInfo rootMotion = new RootMotionInfo(); RootMotionInfo rootMotion = new RootMotionInfo();
var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); float duration = animation.Duration;
float mid = duration * 0.5f;
rootMotion.timeIsPastMid = currentTime > mid;
TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) { if (timeline != null) {
float duration = animation.Duration;
float mid = duration * 0.5f;
rootMotion.start = timeline.Evaluate(0); rootMotion.start = timeline.Evaluate(0);
rootMotion.current = timeline.Evaluate(currentTime); rootMotion.current = timeline.Evaluate(currentTime);
rootMotion.mid = timeline.Evaluate(mid); rootMotion.mid = timeline.Evaluate(mid);
rootMotion.end = timeline.Evaluate(duration); rootMotion.end = timeline.Evaluate(duration);
rootMotion.timeIsPastMid = currentTime > mid; return rootMotion;
}
TranslateXTimeline xTimeline = animation.FindTimelineForBone<TranslateXTimeline>(rootMotionBoneIndex);
TranslateYTimeline yTimeline = animation.FindTimelineForBone<TranslateYTimeline>(rootMotionBoneIndex);
if (xTimeline != null || yTimeline != null) {
rootMotion.start = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0);
rootMotion.current = TimelineExtensions.Evaluate(xTimeline, yTimeline, currentTime);
rootMotion.mid = TimelineExtensions.Evaluate(xTimeline, yTimeline, mid);
rootMotion.end = TimelineExtensions.Evaluate(xTimeline, yTimeline, duration);
return rootMotion;
} }
return rootMotion; return rootMotion;
} }
void FindTransformConstraintsAffectingBone () {
var constraints = skeletonComponent.Skeleton.TransformConstraints;
var constraintsItems = constraints.Items;
for (int i = 0, n = constraints.Count; i < n; ++i) {
TransformConstraint constraint = constraintsItems[i];
if (constraint.Bones.Contains(rootMotionBone)) {
transformConstraintIndices.Add(i);
Bone targetBone = constraint.Target;
Vector2 constraintPos = new Vector2(targetBone.X, targetBone.Y);
transformConstraintLastPos.Add(constraintPos);
}
}
}
Vector2 GetTimelineMovementDelta (float startTime, float endTime, Vector2 GetTimelineMovementDelta (float startTime, float endTime,
TranslateTimeline timeline, Animation animation) { TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, Animation animation) {
Vector2 currentDelta; Vector2 currentDelta;
if (startTime > endTime) // Looped if (startTime > endTime) // Looped
currentDelta = (timeline.Evaluate(animation.Duration) - timeline.Evaluate(startTime)) currentDelta =
+ (timeline.Evaluate(endTime) - timeline.Evaluate(0)); (TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime))
+ (TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, 0));
else if (startTime != endTime) // Non-looped else if (startTime != endTime) // Non-looped
currentDelta = timeline.Evaluate(endTime) - timeline.Evaluate(startTime); currentDelta = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime);
else else
currentDelta = Vector2.zero; currentDelta = Vector2.zero;
return currentDelta; return currentDelta;
@ -267,6 +364,19 @@ namespace Spine.Unity {
} }
} }
void ApplyTransformConstraints () {
rootMotionBone.AX = rootMotionBone.X;
rootMotionBone.AY = rootMotionBone.Y;
var transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items;
foreach (int constraintIndex in this.transformConstraintIndices) {
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
// apply the constraint and sets Bone.ax and Bone.ay values.
/// Update is based on Bone.x and Bone.y, so skeleton.UpdateWorldTransform()
/// can be called afterwards without having a different starting point.
constraint.Update();
}
}
Vector2 GetScaleAffectingRootMotion () { Vector2 GetScaleAffectingRootMotion () {
Vector2 parentBoneScale; Vector2 parentBoneScale;
return GetScaleAffectingRootMotion(out parentBoneScale); return GetScaleAffectingRootMotion(out parentBoneScale);
@ -309,6 +419,9 @@ namespace Spine.Unity {
} }
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) { void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
ApplyTransformConstraints();
// Move top level bones in opposite direction of the root motion bone // Move top level bones in opposite direction of the root motion bone
var skeleton = skeletonComponent.Skeleton; var skeleton = skeletonComponent.Skeleton;
foreach (var topLevelBone in topLevelBones) { foreach (var topLevelBone in topLevelBones) {
@ -316,8 +429,13 @@ namespace Spine.Unity {
if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX; if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX;
if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY; if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY;
} else { } else {
float offsetX = (initialOffset.x - rootMotionBone.X) * parentBoneScale.x; bool useAppliedPosition = transformConstraintIndices.Count > 0;
float offsetY = (initialOffset.y - rootMotionBone.Y) * parentBoneScale.y; float rootMotionBoneX = useAppliedPosition ? rootMotionBone.AX : rootMotionBone.X;
float rootMotionBoneY = useAppliedPosition ? rootMotionBone.AY : rootMotionBone.Y;
float offsetX = (initialOffset.x - rootMotionBoneX) * parentBoneScale.x;
float offsetY = (initialOffset.y - rootMotionBoneY) * parentBoneScale.y;
if (transformPositionX) topLevelBone.X = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX; if (transformPositionX) topLevelBone.X = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX;
if (transformPositionY) topLevelBone.Y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY; if (transformPositionY) topLevelBone.Y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
} }

View File

@ -411,6 +411,7 @@ namespace Spine.Unity {
} }
public bool MatchRectTransformWithBounds () { public bool MatchRectTransformWithBounds () {
if (!wasUpdatedAfterInit) Update(0);
UpdateMesh(); UpdateMesh();
if (!this.allowMultipleCanvasRenderers) if (!this.allowMultipleCanvasRenderers)

View File

@ -181,7 +181,10 @@ namespace Spine.Unity {
var drawOrderItems = drawOrder.Items; var drawOrderItems = drawOrder.Items;
for (int i = 0; i < drawOrderCount; i++) { for (int i = 0; i < drawOrderCount; i++) {
Slot slot = drawOrderItems[i]; Slot slot = drawOrderItems[i];
if (!slot.Bone.Active) continue; if (!slot.Bone.Active) {
workingAttachmentsItems[i] = null;
continue;
}
if (slot.Data.BlendMode == BlendMode.Additive) current.hasPMAAdditiveSlot = true; if (slot.Data.BlendMode == BlendMode.Additive) current.hasPMAAdditiveSlot = true;
Attachment attachment = slot.Attachment; Attachment attachment = slot.Attachment;
@ -303,7 +306,10 @@ namespace Spine.Unity {
var drawOrderItems = drawOrder.Items; var drawOrderItems = drawOrder.Items;
for (int i = 0; i < drawOrderCount; i++) { for (int i = 0; i < drawOrderCount; i++) {
Slot slot = drawOrderItems[i]; Slot slot = drawOrderItems[i];
if (!slot.Bone.Active) continue; if (!slot.Bone.Active) {
workingAttachmentsItems[i] = null;
continue;
}
if (slot.Data.BlendMode == BlendMode.Additive) current.hasPMAAdditiveSlot = true; if (slot.Data.BlendMode == BlendMode.Additive) current.hasPMAAdditiveSlot = true;
Attachment attachment = slot.Attachment; Attachment attachment = slot.Attachment;
#if SPINE_TRIANGLECHECK #if SPINE_TRIANGLECHECK

View File

@ -35,7 +35,7 @@ float4 computeOutlinePixel(sampler2D mainTexture, float2 mainTextureTexelSize,
float average = (pixelTop + pixelBottom + pixelLeft + pixelRight) * vertexColorAlpha / numSamples; float average = (pixelTop + pixelBottom + pixelLeft + pixelRight) * vertexColorAlpha / numSamples;
#endif #endif
float thresholdStart = ThresholdEnd * (1.0 - OutlineSmoothness); float thresholdStart = ThresholdEnd * (1.0 - OutlineSmoothness);
float outlineAlpha = saturate((average - thresholdStart) / (ThresholdEnd - thresholdStart)) - pixelCenter; float outlineAlpha = saturate(saturate((average - thresholdStart) / (ThresholdEnd - thresholdStart)) - pixelCenter);
return lerp(texColor, OutlineColor, outlineAlpha); return lerp(texColor, OutlineColor, outlineAlpha);
} }

View File

@ -193,18 +193,15 @@ namespace Spine.Unity.AttachmentTools {
region.index = -1; region.index = -1;
region.degrees = s.packed && s.packingRotation != SpritePackingRotation.None ? 90 : 0; region.degrees = s.packed && s.packingRotation != SpritePackingRotation.None ? 90 : 0;
// World space units
Bounds bounds = s.bounds;
Vector2 boundsMin = bounds.min, boundsMax = bounds.max;
// Texture space/pixel units // Texture space/pixel units
Rect spineRect = s.rect.SpineUnityFlipRect(s.texture.height); Rect spineRect = s.textureRect.SpineUnityFlipRect(s.texture.height);
Rect originalRect = s.rect;
region.width = (int)spineRect.width; region.width = (int)spineRect.width;
region.originalWidth = (int)spineRect.width; region.originalWidth = (int)originalRect.width;
region.height = (int)spineRect.height; region.height = (int)spineRect.height;
region.originalHeight = (int)spineRect.height; region.originalHeight = (int)originalRect.height;
region.offsetX = spineRect.width * (0.5f - InverseLerp(boundsMin.x, boundsMax.x, 0)); region.offsetX = s.textureRectOffset.x;
region.offsetY = spineRect.height * (0.5f - InverseLerp(boundsMin.y, boundsMax.y, 0)); region.offsetY = s.textureRectOffset.y;
if (isolatedTexture) { if (isolatedTexture) {
region.u = 0; region.u = 0;

View File

@ -293,11 +293,11 @@ namespace Spine.Unity {
break; break;
} }
} }
bool isShaderWithMeshNormals = IsSpriteShader(material); bool isShaderWithMeshNormals = IsLitSpriteShader(material);
return isShaderWithMeshNormals && !anyFixedNormalSet; return isShaderWithMeshNormals && !anyFixedNormalSet;
} }
static bool IsSpriteShader (Material material) { static bool IsLitSpriteShader (Material material) {
string shaderName = material.shader.name; string shaderName = material.shader.name;
return shaderName.Contains("Spine/Sprite/Pixel Lit") || return shaderName.Contains("Spine/Sprite/Pixel Lit") ||
shaderName.Contains("Spine/Sprite/Vertex Lit") || shaderName.Contains("Spine/Sprite/Vertex Lit") ||
@ -305,6 +305,13 @@ namespace Spine.Unity {
shaderName.Contains("Pipeline/Spine/Sprite"); // covers both URP and LWRP shaderName.Contains("Pipeline/Spine/Sprite"); // covers both URP and LWRP
} }
static bool IsSpriteShader (Material material) {
if (IsLitSpriteShader(material))
return true;
string shaderName = material.shader.name;
return shaderName.Contains("Spine/Sprite/Unlit");
}
static bool RequiresTintBlack (Material material) { static bool RequiresTintBlack (Material material) {
bool isTintBlackShader = bool isTintBlackShader =
material.shader.name.Contains("Spine") && material.shader.name.Contains("Spine") &&

View File

@ -233,7 +233,7 @@ namespace Spine.Unity {
buffer = buffer ?? new Vector2[bufferTargetSize]; buffer = buffer ?? new Vector2[bufferTargetSize];
if (buffer.Length < bufferTargetSize) throw new System.ArgumentException(string.Format("Vector2 buffer too small. {0} requires an array of size {1}. Use the attachment's .WorldVerticesLength to get the correct size.", va.Name, floatsCount), "buffer"); if (buffer.Length < bufferTargetSize) throw new System.ArgumentException(string.Format("Vector2 buffer too small. {0} requires an array of size {1}. Use the attachment's .WorldVerticesLength to get the correct size.", va.Name, floatsCount), "buffer");
if (va.Bones == null) { if (va.Bones == null && va.TimelineAttachment == null) {
var localVerts = va.Vertices; var localVerts = va.Vertices;
for (int i = 0; i < bufferTargetSize; i++) { for (int i = 0; i < bufferTargetSize; i++) {
int j = i * 2; int j = i * 2;

View File

@ -36,7 +36,7 @@ namespace Spine.Unity.AnimationTools {
/// <summary>Evaluates the resulting value of a TranslateTimeline at a given time. /// <summary>Evaluates the resulting value of a TranslateTimeline at a given time.
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData. /// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
/// If no SkeletonData is given, values are returned as difference to setup pose /// If no SkeletonData is provided, values are returned as difference to setup pose
/// instead of absolute values.</summary> /// instead of absolute values.</summary>
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) { public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
if (time < timeline.Frames[0]) return Vector2.zero; if (time < timeline.Frames[0]) return Vector2.zero;
@ -52,8 +52,39 @@ namespace Spine.Unity.AnimationTools {
} }
} }
/// <summary>Evaluates the resulting value of a pair of split translate timelines at a given time.
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
/// If no SkeletonData is given, values are returned as difference to setup pose
/// instead of absolute values.</summary>
public static Vector2 Evaluate (TranslateXTimeline xTimeline, TranslateYTimeline yTimeline,
float time, SkeletonData skeletonData = null) {
float x = 0, y = 0;
if (xTimeline != null && time > xTimeline.Frames[0]) x = xTimeline.GetCurveValue(time);
if (yTimeline != null && time > yTimeline.Frames[0]) y = yTimeline.GetCurveValue(time);
if (skeletonData == null) {
return new Vector2(x, y);
} else {
var bonesItems = skeletonData.Bones.Items;
BoneData boneDataX = bonesItems[xTimeline.BoneIndex];
BoneData boneDataY = bonesItems[yTimeline.BoneIndex];
return new Vector2(boneDataX.X + x, boneDataY.Y + y);
}
}
/// <summary>Evaluates the resulting X and Y translate mix values of a
/// TransformConstraintTimeline at a given time.</summary>
public static Vector2 EvaluateTranslateXYMix (this TransformConstraintTimeline timeline, float time) {
if (time < timeline.Frames[0]) return Vector2.zero;
float rotate, mixX, mixY, scaleX, scaleY, shearY;
timeline.GetCurveValue(out rotate, out mixX, out mixY, out scaleX, out scaleY, out shearY, time);
return new Vector2(mixX, mixY);
}
/// <summary>Gets the translate timeline for a given boneIndex. /// <summary>Gets the translate timeline for a given boneIndex.
/// You can get the boneIndex using SkeletonData.FindBoneIndex. /// You can get the boneIndex using SkeletonData.FindBone().Index.
/// The root bone is always boneIndex 0. /// The root bone is always boneIndex 0.
/// This will return null if a TranslateTimeline is not found.</summary> /// This will return null if a TranslateTimeline is not found.</summary>
public static TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) { public static TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) {
@ -67,5 +98,35 @@ namespace Spine.Unity.AnimationTools {
} }
return null; return null;
} }
/// <summary>Gets the IBoneTimeline timeline of a given type for a given boneIndex.
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
/// The root bone is always boneIndex 0.
/// This will return null if a timeline of the given type is not found.</summary>
public static T FindTimelineForBone<T> (this Animation a, int boneIndex) where T : class, IBoneTimeline {
foreach (var timeline in a.Timelines) {
T translateTimeline = timeline as T;
if (translateTimeline != null && translateTimeline.BoneIndex == boneIndex)
return translateTimeline;
}
return null;
}
/// <summary>Gets the transform constraint timeline for a given boneIndex.
/// You can get the boneIndex using SkeletonData.FindBone().Index.
/// The root bone is always boneIndex 0.
/// This will return null if a TranslateTimeline is not found.</summary>
public static TransformConstraintTimeline FindTransformConstraintTimeline (this Animation a, int transformConstraintIndex) {
foreach (var timeline in a.Timelines) {
if (timeline.GetType().IsSubclassOf(typeof(TransformConstraintTimeline)))
continue;
var transformConstraintTimeline = timeline as TransformConstraintTimeline;
if (transformConstraintTimeline != null &&
transformConstraintTimeline.TransformConstraintIndex == transformConstraintIndex)
return transformConstraintTimeline;
}
return null;
}
} }
} }

View File

@ -1,13 +0,0 @@
{
"name": "SpineTests",
"optionalUnityReferences": [
"TestAssemblies"
],
"references" : [
"spine-unity",
"spine-csharp"
],
"includePlatforms": [
"Editor"
]
}

View File

@ -1 +0,0 @@
Add the directory content of 'spine-csharp/tests' as 'tests' here (e.g. using a symlink).

View File

@ -39,7 +39,20 @@ namespace Spine.Unity.Tests {
public void RunAnimationStateTestsSimplePasses () { public void RunAnimationStateTestsSimplePasses () {
AnimationStateTests.logImplementation += Log; AnimationStateTests.logImplementation += Log;
AnimationStateTests.failImplementation += Fail; AnimationStateTests.failImplementation += Fail;
AnimationStateTests.Main("Assets/SpineTests/spine-csharp-tests/tests/assets/test.json");
string testJsonFilename = "test";
string testJsonPathEnd = "tests/assets/" + testJsonFilename + ".json";
var guids = UnityEditor.AssetDatabase.FindAssets(testJsonFilename + " t:textasset");
if (guids.Length <= 0) Fail(testJsonFilename + ".json asset not found.");
foreach (var guid in guids) {
string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
if (assetPath.EndsWith(testJsonPathEnd)) {
AnimationStateTests.Main(assetPath);
return;
}
}
Fail(testJsonPathEnd + " not found.");
} }
public void Log (string message) { public void Log (string message) {

View File

@ -0,0 +1,26 @@
{
"name": "spine-unity-tests.Editor.Tests",
"rootNamespace": "",
"references": [
"spine-unity",
"spine-csharp",
"spine-csharp-tests",
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,36 @@
{
"name": "com.esotericsoftware.spine.spine-unity-tests",
"displayName": "spine-unity Runtime Tests",
"description": "This plugin provides tests for the spine-unity runtime.",
"version": "4.1.0",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",
"email": "contact@esotericsoftware.com",
"url": "http://esotericsoftware.com/"
},
"dependencies": {
"com.esotericsoftware.spine.spine-csharp-tests": "4.1.0",
"com.esotericsoftware.spine.spine-csharp": "4.1.0",
"com.esotericsoftware.spine.spine-unity": "4.1.0"
},
"repository": {
"type": "git",
"url": "git@github.com:EsotericSoftware/spine-runtimes.git"
},
"keywords": [
"spine",
"spine-unity",
"runtimes",
"tests",
"2d",
"skeletal",
"animation"
],
"license": "SEE LICENSE IN LICENSE",
"bugs": {
"url": "https://github.com/EsotericSoftware/spine-runtimes/issues"
},
"homepage": "https://github.com/EsotericSoftware/spine-runtimes#readme",
"type": "tests"
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 11052e5422ef2be4eae7b875ba449c31
PackageManifestImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1 @@
Add the directory content of 'spine-csharp/tests' as 'tests' here (e.g. using a symlink), or import the package via the `package.json` file located in `spine-csharp/tests`.

View File

@ -62,10 +62,8 @@ namespace Spine.Unity.Playables {
} }
public override void OnGraphStop (Playable playable) { public override void OnGraphStop (Playable playable) {
if (endAtClipEnd) if (!isPaused && endAtClipEnd)
HandleClipEnd(); HandleClipEnd();
else if (isPaused) // stop event occurred after pause, so resume again
HandleResume(playable);
} }
public override void OnBehaviourPlay (Playable playable, FrameData info) { public override void OnBehaviourPlay (Playable playable, FrameData info) {

View File

@ -48,9 +48,39 @@ namespace Spine.Unity.Playables {
} }
var scriptPlayable = ScriptPlayable<SpineAnimationStateMixerBehaviour>.Create(graph, inputCount); var scriptPlayable = ScriptPlayable<SpineAnimationStateMixerBehaviour>.Create(graph, inputCount);
#if UNITY_EDITOR
WarnIfDuplicateTrackIndex();
#endif
var mixerBehaviour = scriptPlayable.GetBehaviour(); var mixerBehaviour = scriptPlayable.GetBehaviour();
mixerBehaviour.trackIndex = this.trackIndex; mixerBehaviour.trackIndex = this.trackIndex;
return scriptPlayable; return scriptPlayable;
} }
#if UNITY_EDITOR
static float lastWarningTime = 0;
public void WarnIfDuplicateTrackIndex () {
if (Time.frameCount == lastWarningTime) // only warn once.
return;
lastWarningTime = Time.frameCount;
var rootTracks = timelineAsset.GetRootTracks();
List<int> trackIndices = new List<int>();
int trackFromTop = -1; // first invisible track is marker track, skipped.
foreach (var track in rootTracks) {
++trackFromTop;
if (track.GetType() != typeof(SpineAnimationStateTrack))
continue;
var animationStateTrack = (SpineAnimationStateTrack)track;
int trackIndex = animationStateTrack.trackIndex;
if (trackIndices.Contains(trackIndex)) {
Debug.LogWarning(string.Format("Please change the 'Track Index' Inspector property " +
"at Track number {0} from the top, both tracks are setting animations at track index '{1}'.",
trackFromTop, trackIndex));
} else
trackIndices.Add(trackIndex);
}
}
#endif
} }
} }

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.timeline", "name": "com.esotericsoftware.spine.timeline",
"displayName": "Spine Timeline Extensions", "displayName": "Spine Timeline Extensions",
"description": "This plugin provides integration of spine-unity for the Unity Timeline.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime (via the spine-unity unitypackage), version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", "description": "This plugin provides integration of spine-unity for the Unity Timeline.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime (via the spine-unity unitypackage), version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
"version": "4.1.0", "version": "4.1.2",
"unity": "2018.3", "unity": "2018.3",
"author": { "author": {
"name": "Esoteric Software", "name": "Esoteric Software",

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.timeline", "name": "com.esotericsoftware.spine.timeline",
"displayName": "Spine Timeline Extensions", "displayName": "Spine Timeline Extensions",
"description": "This plugin provides integration of spine-unity for the Unity Timeline.\n\nPrerequisites:\nIt requires a working installation of the spine-unity and spine-csharp runtimes as UPM packages (not as spine-unity unitypackage), version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", "description": "This plugin provides integration of spine-unity for the Unity Timeline.\n\nPrerequisites:\nIt requires a working installation of the spine-unity and spine-csharp runtimes as UPM packages (not as spine-unity unitypackage), version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
"version": "4.1.0", "version": "4.1.2",
"unity": "2018.3", "unity": "2018.3",
"author": { "author": {
"name": "Esoteric Software", "name": "Esoteric Software",