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
## 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

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.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.skel "$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.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.skel "$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"
$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_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_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"
cp -f ../goblins/export/goblins-pro.json "$UNITY_TARGET_DIR/goblins.json"

View File

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

View File

@ -48,9 +48,9 @@ namespace spine {
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);

View File

@ -51,6 +51,8 @@ namespace spine {
class ClippingAttachment;
class Sequence;
class SP_API AttachmentLoader : public SpineObject {
public:
RTTI_DECL
@ -60,10 +62,10 @@ namespace spine {
virtual ~AttachmentLoader();
/// @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.
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.
virtual BoundingBoxAttachment *newBoundingBoxAttachment(Skin &skin, const String &name) = 0;

View File

@ -31,6 +31,8 @@
#define Spine_MeshAttachment_h
#include <spine/VertexAttachment.h>
#include <spine/TextureRegion.h>
#include <spine/Sequence.h>
#include <spine/Vector.h>
#include <spine/Color.h>
#include <spine/HasRendererObject.h>
@ -51,7 +53,10 @@ namespace spine {
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();
@ -59,7 +64,7 @@ namespace spine {
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<unsigned short> &getTriangles();
@ -70,52 +75,13 @@ namespace spine {
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);
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);
void setSequence(Sequence *sequence);
MeshAttachment *getParentMesh();
@ -137,22 +103,17 @@ namespace spine {
MeshAttachment *newLinkedMesh();
private:
float _regionOffsetX, _regionOffsetY, _regionWidth, _regionHeight, _regionOriginalWidth, _regionOriginalHeight;
MeshAttachment *_parentMesh;
Vector<float> _uvs;
Vector<float> _regionUVs;
Vector<unsigned short> _triangles;
Vector<unsigned short> _edges;
String _path;
float _regionU;
float _regionV;
float _regionU2;
float _regionV2;
float _width;
float _height;
Color _color;
int _hullLength;
int _regionDegrees;
int _width, _height;
TextureRegion *_region;
Sequence *_sequence;
};
}

View File

@ -33,6 +33,8 @@
#include <spine/Attachment.h>
#include <spine/Vector.h>
#include <spine/Color.h>
#include <spine/Sequence.h>
#include <spine/TextureRegion.h>
#include <spine/HasRendererObject.h>
@ -54,7 +56,9 @@ namespace spine {
public:
explicit RegionAttachment(const String &name);
void updateOffset();
virtual ~RegionAttachment();
void updateRegion();
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 offset The worldVertices index to begin writing values.
/// @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();
@ -101,29 +105,13 @@ namespace spine {
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);
float getRegionWidth();
void setRegionWidth(float inValue);
float getRegionHeight();
void setRegionHeight(float inValue);
float getRegionOriginalWidth();
void setRegionOriginalWidth(float inValue);
float getRegionOriginalHeight();
void setRegionOriginalHeight(float inValue);
void setSequence(Sequence *sequence);
Vector<float> &getOffset();
@ -142,15 +130,12 @@ namespace spine {
static const int BRY;
float _x, _y, _rotation, _scaleX, _scaleY, _width, _height;
float _regionOffsetX, _regionOffsetY, _regionWidth, _regionHeight, _regionOriginalWidth, _regionOriginalHeight;
Vector<float> _vertexOffset;
Vector<float> _uvs;
String _path;
float _regionU;
float _regionV;
float _regionU2;
float _regionV2;
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.
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.
/// @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.
@ -204,10 +202,6 @@ namespace spine {
Color &getColor();
float getTime();
void setTime(float inValue);
void setPosition(float x, float y);
float getX();
@ -237,7 +231,6 @@ namespace spine {
Vector<Updatable *> _updateCache;
Skin *_skin;
Color _color;
float _time;
float _scaleX, _scaleY;
float _x, _y;

View File

@ -114,12 +114,12 @@ namespace spine {
void setAttachmentState(int state);
float getAttachmentTime();
void setAttachmentTime(float inValue);
Vector<float> &getDeform();
int getSequenceIndex();
void setSequenceIndex(int index);
private:
SlotData &_data;
Bone &_bone;
@ -129,7 +129,7 @@ namespace spine {
bool _hasDarkColor;
Attachment *_attachment;
int _attachmentState;
float _attachmentTime;
int _sequenceIndex;
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 offset The worldVertices index to begin writing values.
/// @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);
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);
/// Gets a unique ID for this attachment.
@ -79,9 +79,9 @@ namespace spine {
void setWorldVerticesLength(size_t inValue);
VertexAttachment *getDeformAttachment();
Attachment * getTimelineAttachment();
void setDeformAttachment(VertexAttachment *attachment);
void setTimelineAttachment(Attachment *attachment);
void copyTo(VertexAttachment *other);
@ -89,7 +89,7 @@ namespace spine {
Vector <size_t> _bones;
Vector<float> _vertices;
size_t _worldVerticesLength;
VertexAttachment *_deformAttachment;
Attachment *_timelineAttachment;
private:
const int _id;

View File

@ -48,53 +48,44 @@ namespace spine {
AtlasAttachmentLoader::AtlasAttachmentLoader(Atlas *atlas) : AttachmentLoader(), _atlas(atlas) {
}
RegionAttachment *AtlasAttachmentLoader::newRegionAttachment(Skin &skin, const String &name, const String &path) {
SP_UNUSED(skin);
AtlasRegion *regionP = findRegion(path);
if (!regionP) return NULL;
AtlasRegion &region = *regionP;
RegionAttachment *attachmentP = new (__FILE__, __LINE__) RegionAttachment(name);
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;
bool loadSequence (Atlas *atlas, const String &basePath, Sequence *sequence) {
Vector<TextureRegion *> &regions = sequence->getRegions();
for (int i = 0, n = regions.size(); i < n; i++) {
String path = sequence->getPath(basePath, i);
regions[i] = atlas->findRegion(path);
if (!regions[i]) return false;
regions[i]->rendererObject = regions[i];
}
return true;
}
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);
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);
if (!regionP) return NULL;
MeshAttachment *AtlasAttachmentLoader::newMeshAttachment(Skin &skin, const String &name, const String &path, Sequence *sequence) {
SP_UNUSED(skin);
MeshAttachment *attachment = new (__FILE__, __LINE__) MeshAttachment(name);
AtlasRegion &region = *regionP;
MeshAttachment *attachmentP = new (__FILE__, __LINE__) MeshAttachment(name);
MeshAttachment &attachment = *attachmentP;
attachment.setRendererObject(regionP);
attachment._regionU = region.u;
attachment._regionV = region.v;
attachment._regionU2 = region.u2;
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;
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;
}
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);
if (attachment->_deformAttachment != _attachment) {
if (attachment->_timelineAttachment != _attachment) {
return;
}

View File

@ -39,43 +39,36 @@ using namespace spine;
RTTI_IMPL(MeshAttachment, VertexAttachment)
MeshAttachment::MeshAttachment(const String &name) : VertexAttachment(name), HasRendererObject(),
_regionOffsetX(0),
_regionOffsetY(0),
_regionWidth(0),
_regionHeight(0),
_regionOriginalWidth(0),
_regionOriginalHeight(0),
_parentMesh(NULL),
_path(),
_regionU(0),
_regionV(0),
_regionU2(0),
_regionV2(0),
_width(0),
_height(0),
_color(1, 1, 1, 1),
_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()) {
_uvs.setSize(_regionUVs.size(), 0);
}
int i = 0, n = _regionUVs.size();
float u = _regionU, v = _regionV;
float u = _region->u, v = _region->v;
float width = 0, height = 0;
switch (_regionDegrees) {
switch (_region->degrees) {
case 90: {
float textureWidth = _regionHeight / (_regionU2 - _regionU);
float textureHeight = _regionWidth / (_regionV2 - _regionV);
u -= (_regionOriginalHeight - _regionOffsetY - _regionHeight) / textureWidth;
v -= (_regionOriginalWidth - _regionOffsetX - _regionWidth) / textureHeight;
width = _regionOriginalHeight / textureWidth;
height = _regionOriginalWidth / textureHeight;
float textureWidth = _region->height / (_region->u2 - _region->u);
float textureHeight = _region->width / (_region->v2 - _region->v);
u -= (_region->originalHeight - _region->offsetY - _region->height) / textureWidth;
v -= (_region->originalWidth - _region->offsetX - _region->width) / textureHeight;
width = _region->originalHeight / textureWidth;
height = _region->originalWidth / textureHeight;
for (i = 0; i < n; i += 2) {
_uvs[i] = u + _regionUVs[i + 1] * width;
_uvs[i + 1] = v + (1 - _regionUVs[i]) * height;
@ -83,12 +76,12 @@ void MeshAttachment::updateUVs() {
return;
}
case 180: {
float textureWidth = _regionWidth / (_regionU2 - _regionU);
float textureHeight = _regionHeight / (_regionV2 - _regionV);
u -= (_regionOriginalWidth - _regionOffsetX - _regionWidth) / textureWidth;
v -= _regionOffsetY / textureHeight;
width = _regionOriginalWidth / textureWidth;
height = _regionOriginalHeight / textureHeight;
float textureWidth = _region->width / (_region->u2 - _region->u);
float textureHeight = _region->height / (_region->v2 - _region->v);
u -= (_region->originalWidth - _region->offsetX - _region->width) / textureWidth;
v -= _region->offsetY / textureHeight;
width = _region->originalWidth / textureWidth;
height = _region->originalHeight / textureHeight;
for (i = 0; i < n; i += 2) {
_uvs[i] = u + (1 - _regionUVs[i]) * width;
_uvs[i + 1] = v + (1 - _regionUVs[i + 1]) * height;
@ -96,12 +89,12 @@ void MeshAttachment::updateUVs() {
return;
}
case 270: {
float textureHeight = _regionHeight / (_regionV2 - _regionV);
float textureWidth = _regionWidth / (_regionU2 - _regionU);
u -= _regionOffsetY / textureWidth;
v -= _regionOffsetX / textureHeight;
width = _regionOriginalHeight / textureWidth;
height = _regionOriginalWidth / textureHeight;
float textureHeight = _region->height / (_region->v2 - _region->v);
float textureWidth = _region->width / (_region->u2 - _region->u);
u -= _region->offsetY / textureWidth;
v -= _region->offsetX / textureHeight;
width = _region->originalHeight / textureWidth;
height = _region->originalWidth / textureHeight;
for (i = 0; i < n; i += 2) {
_uvs[i] = u + (1 - _regionUVs[i + 1]) * width;
_uvs[i + 1] = v + _regionUVs[i] * height;
@ -109,12 +102,12 @@ void MeshAttachment::updateUVs() {
return;
}
default: {
float textureWidth = _regionWidth / (_regionU2 - _regionU);
float textureHeight = _regionHeight / (_regionV2 - _regionV);
u -= _regionOffsetX / textureWidth;
v -= (_regionOriginalHeight - _regionOffsetY - _regionHeight) / textureHeight;
width = _regionOriginalWidth / textureWidth;
height = _regionOriginalHeight / textureHeight;
float textureWidth = _region->width / (_region->u2 - _region->u);
float textureHeight = _region->height / (_region->v2 - _region->v);
u -= _region->offsetX / textureWidth;
v -= (_region->originalHeight - _region->offsetY - _region->height) / textureHeight;
width = _region->originalWidth / textureWidth;
height = _region->originalHeight / textureHeight;
for (i = 0; i < n; i += 2) {
_uvs[i] = u + _regionUVs[i] * width;
_uvs[i + 1] = v + _regionUVs[i + 1] * height;
@ -151,94 +144,6 @@ void MeshAttachment::setPath(const String &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() {
return _parentMesh;
}
@ -287,17 +192,8 @@ Attachment *MeshAttachment::copy() {
MeshAttachment *copy = new (__FILE__, __LINE__) MeshAttachment(getName());
copy->setRendererObject(getRendererObject());
copy->_regionU = _regionU;
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->setRegion(_region);
copy->setSequence(_sequence != NULL ? _sequence->copy() : NULL);
copy->_path = _path;
copy->_color.set(_color);
@ -317,21 +213,17 @@ Attachment *MeshAttachment::copy() {
MeshAttachment *MeshAttachment::newLinkedMesh() {
MeshAttachment *copy = new (__FILE__, __LINE__) MeshAttachment(getName());
copy->setRendererObject(getRendererObject());
copy->_regionU = _regionU;
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->setRegion(_region);
copy->_path = _path;
copy->_color.set(_color);
copy->_deformAttachment = this->_deformAttachment;
copy->_timelineAttachment = this->_timelineAttachment;
copy->setParentMesh(_parentMesh ? _parentMesh : this);
copy->updateUVs();
if (copy->_region) copy->updateRegion();
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/Bone.h>
#include <spine/Slot.h>
#include <assert.h>
@ -58,29 +59,25 @@ RegionAttachment::RegionAttachment(const String &name) : Attachment(name), HasRe
_scaleY(1),
_width(0),
_height(0),
_regionOffsetX(0),
_regionOffsetY(0),
_regionWidth(0),
_regionHeight(0),
_regionOriginalWidth(0),
_regionOriginalHeight(0),
_path(),
_regionU(0),
_regionV(0),
_regionU2(0),
_regionV2(0),
_color(1, 1, 1, 1) {
_color(1, 1, 1, 1),
_region(NULL),
_sequence(NULL) {
_vertexOffset.setSize(NUM_UVS, 0);
_uvs.setSize(NUM_UVS, 0);
}
void RegionAttachment::updateOffset() {
float regionScaleX = _width / _regionOriginalWidth * _scaleX;
float regionScaleY = _height / _regionOriginalHeight * _scaleY;
float localX = -_width / 2 * _scaleX + _regionOffsetX * regionScaleX;
float localY = -_height / 2 * _scaleY + _regionOffsetY * regionScaleY;
float localX2 = localX + _regionWidth * regionScaleX;
float localY2 = localY + _regionHeight * regionScaleY;
RegionAttachment::~RegionAttachment() {
if (_sequence) delete _sequence;
}
void RegionAttachment::updateRegion() {
float regionScaleX = _width / _region->originalWidth * _scaleX;
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 sin = MathUtil::sinDeg(_rotation);
float localXCos = localX * cos + _x;
@ -100,36 +97,37 @@ void RegionAttachment::updateOffset() {
_vertexOffset[URY] = localY2Cos + localX2Sin;
_vertexOffset[BRX] = localX2Cos - localYSin;
_vertexOffset[BRY] = localYCos + localX2Sin;
}
void RegionAttachment::setUVs(float u, float v, float u2, float v2, float degrees) {
if (degrees == 90) {
_uvs[URX] = u;
_uvs[URY] = v2;
_uvs[BRX] = u;
_uvs[BRY] = v;
_uvs[BLX] = u2;
_uvs[BLY] = v;
_uvs[ULX] = u2;
_uvs[ULY] = v2;
if (_region->degrees == 90) {
_uvs[URX] = _region->u;
_uvs[URY] = _region->v2;
_uvs[BRX] = _region->u;
_uvs[BRY] = _region->v;
_uvs[BLX] = _region->u2;
_uvs[BLY] = _region->v;
_uvs[ULX] = _region->u2;
_uvs[ULY] = _region->v2;
} else {
_uvs[ULX] = u;
_uvs[ULY] = v2;
_uvs[URX] = u;
_uvs[URY] = v;
_uvs[BRX] = u2;
_uvs[BRY] = v;
_uvs[BLX] = u2;
_uvs[BLY] = v2;
_uvs[ULX] = _region->u;
_uvs[ULY] = _region->v2;
_uvs[URX] = _region->u;
_uvs[URY] = _region->v;
_uvs[BRX] = _region->u2;
_uvs[BRY] = _region->v;
_uvs[BLX] = _region->u2;
_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));
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 a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD();
float offsetX, offsetY;
@ -222,52 +220,20 @@ void RegionAttachment::setPath(const String &inValue) {
_path = inValue;
}
float RegionAttachment::getRegionOffsetX() {
return _regionOffsetX;
TextureRegion *RegionAttachment::getRegion() {
return _region;
}
void RegionAttachment::setRegionOffsetX(float inValue) {
_regionOffsetX = inValue;
void RegionAttachment::setRegion(TextureRegion *region) {
_region = region;
}
float RegionAttachment::getRegionOffsetY() {
return _regionOffsetY;
Sequence *RegionAttachment::getSequence() {
return _sequence;
}
void RegionAttachment::setRegionOffsetY(float inValue) {
_regionOffsetY = inValue;
}
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;
void RegionAttachment::setSequence(Sequence *sequence) {
_sequence = sequence;
}
Vector<float> &RegionAttachment::getOffset() {
@ -284,12 +250,7 @@ spine::Color &RegionAttachment::getColor() {
Attachment *RegionAttachment::copy() {
RegionAttachment *copy = new (__FILE__, __LINE__) RegionAttachment(getName());
copy->_regionWidth = _regionWidth;
copy->_regionHeight = _regionHeight;
copy->_regionOffsetX = _regionOffsetX;
copy->_regionOffsetY = _regionOffsetY;
copy->_regionOriginalWidth = _regionOriginalWidth;
copy->_regionOriginalHeight = _regionOriginalHeight;
copy->_region = _region;
copy->setRendererObject(getRendererObject());
copy->_path = _path;
copy->_x = _x;
@ -302,5 +263,6 @@ Attachment *RegionAttachment::copy() {
copy->_uvs.clearAndAddAll(_uvs);
copy->_vertexOffset.clearAndAddAll(_vertexOffset);
copy->_color.set(_color);
copy->_sequence = _sequence != NULL ? _sequence->copy() : NULL;
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),
_skin(NULL),
_color(1, 1, 1, 1),
_time(0),
_scaleX(1),
_scaleY(1),
_x(0),
@ -427,10 +426,6 @@ PathConstraint *Skeleton::findPathConstraint(const String &constraintName) {
return NULL;
}
void Skeleton::update(float delta) {
_time += delta;
}
void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHeight, Vector<float> &outVertexBuffer) {
float minX = 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) {
outVertexBuffer.setSize(8, 0);
}
regionAttachment->computeWorldVertices(slot->getBone(), outVertexBuffer, 0);
regionAttachment->computeWorldVertices(*slot, outVertexBuffer, 0);
} else if (attachment != NULL && attachment->getRTTI().instanceOf(MeshAttachment::rtti)) {
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);
}
mesh->computeWorldVertices(*slot, 0, verticesLength, outVertexBuffer, 0);
mesh->computeWorldVertices(*slot, 0, verticesLength, outVertexBuffer.buffer(), 0);
}
for (size_t ii = 0; ii < verticesLength; ii += 2) {
@ -523,14 +518,6 @@ Color &Skeleton::getColor() {
return _color;
}
float Skeleton::getTime() {
return _time;
}
void Skeleton::setTime(float inValue) {
_time = inValue;
}
void Skeleton::setPosition(float x, float y) {
_x = x;
_y = y;

View File

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

View File

@ -569,7 +569,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
color = Json::getString(attachmentMap, "color", 0);
if (color) toColor(region->getColor(), color, true);
region->updateOffset();
region->updateRegion();
_attachmentLoader->configureAttachment(region);
break;
}
@ -610,7 +610,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
readVertices(attachmentMap, mesh, verticesLength);
mesh->updateUVs();
mesh->updateRegion();
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());
return NULL;
}
linkedMesh->_mesh->_deformAttachment = linkedMesh->_inheritDeform ? static_cast<VertexAttachment *>(parent)
: linkedMesh->_mesh;
linkedMesh->_mesh->_timelineAttachment = linkedMesh->_inheritDeform ? static_cast<VertexAttachment *>(parent)
: linkedMesh->_mesh;
linkedMesh->_mesh->setParentMesh(static_cast<MeshAttachment *>(parent));
linkedMesh->_mesh->updateUVs();
linkedMesh->_mesh->updateRegion();
_attachmentLoader->configureAttachment(linkedMesh->_mesh);
}
ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes);

View File

@ -49,7 +49,7 @@ Slot::Slot(SlotData &data, Bone &bone) : _data(data),
_hasDarkColor(data.hasDarkColor()),
_attachment(NULL),
_attachmentState(0),
_attachmentTime(0) {
_sequenceIndex(0) {
setToSetupPose();
}
@ -103,12 +103,13 @@ void Slot::setAttachment(Attachment *inValue) {
!_attachment ||
!inValue->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();
}
_attachment = inValue;
_attachmentTime = _skeleton.getTime();
_sequenceIndex = -1;
}
int Slot::getAttachmentState() {
@ -119,14 +120,14 @@ void Slot::setAttachmentState(int state) {
_attachmentState = state;
}
float Slot::getAttachmentTime() {
return _skeleton.getTime() - _attachmentTime;
}
void Slot::setAttachmentTime(float inValue) {
_attachmentTime = _skeleton.getTime() - inValue;
}
Vector<float> &Slot::getDeform() {
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)
VertexAttachment::VertexAttachment(const String &name) : Attachment(name), _worldVerticesLength(0),
_deformAttachment(this), _id(getNextID()) {
_timelineAttachment(this), _id(getNextID()) {
}
VertexAttachment::~VertexAttachment() {
@ -150,12 +150,12 @@ void VertexAttachment::setWorldVerticesLength(size_t inValue) {
_worldVerticesLength = inValue;
}
VertexAttachment *VertexAttachment::getDeformAttachment() {
return _deformAttachment;
Attachment * VertexAttachment::getTimelineAttachment() {
return _timelineAttachment;
}
void VertexAttachment::setDeformAttachment(VertexAttachment *attachment) {
_deformAttachment = attachment;
void VertexAttachment::setTimelineAttachment(Attachment *attachment) {
_timelineAttachment = attachment;
}
int VertexAttachment::getNextID() {
@ -167,5 +167,5 @@ void VertexAttachment::copyTo(VertexAttachment *other) {
other->_bones.clearAndAddAll(this->_bones);
other->_vertices.clearAndAddAll(this->_vertices);
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;
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];
switch (curveType) {
case LINEAR:
@ -2383,23 +2407,6 @@ namespace Spine {
shearY = GetBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
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
guid: f5588a995395d7d428bb39ca0bfb7bd8
guid: 1a1ea245fb673db4ba7bda20b0ed06e7
folderAsset: yes
DefaultImporter:
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": [
{ "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) {
SP_UNUSED(atlas);
@ -610,6 +644,7 @@ DebugExtension dbgExtension(SpineExtension::getInstance());
int main() {
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(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);

View File

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

View File

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

View File

@ -38,7 +38,6 @@ namespace Spine.Unity.Editor {
public class SkeletonAnimationInspector : SkeletonRendererInspector {
protected SerializedProperty animationName, loop, timeScale, autoReset;
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 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();
serializedObject.ApplyModifiedProperties();
if (!isInspectingPrefab) {
if (requireRepaint) {
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
requireRepaint = false;
}
}
}
protected void TrySetAnimation (SkeletonAnimation skeletonAnimation) {

View File

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

View File

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

View File

@ -96,8 +96,7 @@ namespace Spine.Unity {
if (hasBinaryExtension) {
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);
}
else {
} else {
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);
}
@ -110,19 +109,16 @@ namespace Spine.Unity {
using (var memStream = new MemoryStream(asset.bytes)) {
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);
isSpineSkeletonData = false;
return null;
}
}
else {
} else {
Match match = jsonVersionRegex.Match(asset.text);
if (match != null) {
fileVersion.rawVersion = match.Groups[1].Value;
}
else {
} else {
object obj = Json.Deserialize(new StringReader(asset.text));
if (obj == null) {
problemDescription = string.Format("'{0}' is not valid JSON.", asset.name);
@ -156,8 +152,7 @@ namespace Spine.Unity {
try {
fileVersion.version = new[]{ int.Parse(versionSplit[0], 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);
isSpineSkeletonData = false;
return null;
@ -179,9 +174,11 @@ namespace Spine.Unity {
if (char.IsWhiteSpace(c))
continue;
if (!openingBraceFound) {
if (c == '{') openingBraceFound = true;
if (c == '{' || c == '[') openingBraceFound = true;
else return false;
} else
} else if (c == '{' || c == '[' || c == ']' || c == '}' || c == ',')
continue;
else
return c == '"';
}
return true;

View File

@ -66,6 +66,8 @@ namespace Spine.Unity {
protected ISkeletonComponent skeletonComponent;
protected Bone rootMotionBone;
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 Vector2 initialOffset = Vector2.zero;
protected Vector2 tempSkeletonDisplacement;
@ -94,7 +96,6 @@ namespace Spine.Unity {
return; // Root motion is only applied when component is enabled.
if (rigidBody2D != null) {
Vector2 gravityAndVelocityMovement = Vector2.zero;
if (applyRigidbody2DGravity) {
float deltaTime = Time.fixedDeltaTime;
@ -107,11 +108,11 @@ namespace Spine.Unity {
rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y)
+ rigidbodyDisplacement);
}
if (rigidBody != null) {
} else if (rigidBody != null) {
rigidBody.MovePosition(transform.position
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
}
} else return;
Vector2 parentBoneScale;
GetScaleAffectingRootMotion(out parentBoneScale);
ClearEffectiveBoneOffsets(parentBoneScale);
@ -155,6 +156,7 @@ namespace Spine.Unity {
if (bone != null) {
this.rootMotionBoneIndex = bone.Data.Index;
this.rootMotionBone = bone;
FindTransformConstraintsAffectingBone();
} else {
Debug.Log("Bone named \"" + name + "\" could not be found.");
this.rootMotionBoneIndex = 0;
@ -196,37 +198,132 @@ namespace Spine.Unity {
public Vector2 GetAnimationRootMotion (float startTime, float endTime,
Animation animation) {
var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
return GetTimelineMovementDelta(startTime, endTime, timeline, animation);
if (startTime == endTime)
return Vector2.zero;
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) {
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) {
float duration = animation.Duration;
float mid = duration * 0.5f;
rootMotion.start = timeline.Evaluate(0);
rootMotion.current = timeline.Evaluate(currentTime);
rootMotion.mid = timeline.Evaluate(mid);
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;
}
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,
TranslateTimeline timeline, Animation animation) {
TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, Animation animation) {
Vector2 currentDelta;
if (startTime > endTime) // Looped
currentDelta = (timeline.Evaluate(animation.Duration) - timeline.Evaluate(startTime))
+ (timeline.Evaluate(endTime) - timeline.Evaluate(0));
currentDelta =
(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
currentDelta = timeline.Evaluate(endTime) - timeline.Evaluate(startTime);
currentDelta = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime);
else
currentDelta = Vector2.zero;
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 parentBoneScale;
return GetScaleAffectingRootMotion(out parentBoneScale);
@ -309,6 +419,9 @@ namespace Spine.Unity {
}
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
ApplyTransformConstraints();
// Move top level bones in opposite direction of the root motion bone
var skeleton = skeletonComponent.Skeleton;
foreach (var topLevelBone in topLevelBones) {
@ -316,8 +429,13 @@ namespace Spine.Unity {
if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX;
if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY;
} else {
float offsetX = (initialOffset.x - rootMotionBone.X) * parentBoneScale.x;
float offsetY = (initialOffset.y - rootMotionBone.Y) * parentBoneScale.y;
bool useAppliedPosition = transformConstraintIndices.Count > 0;
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 (transformPositionY) topLevelBone.Y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
}

View File

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

View File

@ -181,7 +181,10 @@ namespace Spine.Unity {
var drawOrderItems = drawOrder.Items;
for (int i = 0; i < drawOrderCount; 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;
Attachment attachment = slot.Attachment;
@ -303,7 +306,10 @@ namespace Spine.Unity {
var drawOrderItems = drawOrder.Items;
for (int i = 0; i < drawOrderCount; 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;
Attachment attachment = slot.Attachment;
#if SPINE_TRIANGLECHECK

View File

@ -35,7 +35,7 @@ float4 computeOutlinePixel(sampler2D mainTexture, float2 mainTextureTexelSize,
float average = (pixelTop + pixelBottom + pixelLeft + pixelRight) * vertexColorAlpha / numSamples;
#endif
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);
}

View File

@ -193,18 +193,15 @@ namespace Spine.Unity.AttachmentTools {
region.index = -1;
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
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.originalWidth = (int)spineRect.width;
region.originalWidth = (int)originalRect.width;
region.height = (int)spineRect.height;
region.originalHeight = (int)spineRect.height;
region.offsetX = spineRect.width * (0.5f - InverseLerp(boundsMin.x, boundsMax.x, 0));
region.offsetY = spineRect.height * (0.5f - InverseLerp(boundsMin.y, boundsMax.y, 0));
region.originalHeight = (int)originalRect.height;
region.offsetX = s.textureRectOffset.x;
region.offsetY = s.textureRectOffset.y;
if (isolatedTexture) {
region.u = 0;

View File

@ -293,11 +293,11 @@ namespace Spine.Unity {
break;
}
}
bool isShaderWithMeshNormals = IsSpriteShader(material);
bool isShaderWithMeshNormals = IsLitSpriteShader(material);
return isShaderWithMeshNormals && !anyFixedNormalSet;
}
static bool IsSpriteShader (Material material) {
static bool IsLitSpriteShader (Material material) {
string shaderName = material.shader.name;
return shaderName.Contains("Spine/Sprite/Pixel Lit") ||
shaderName.Contains("Spine/Sprite/Vertex Lit") ||
@ -305,6 +305,13 @@ namespace Spine.Unity {
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) {
bool isTintBlackShader =
material.shader.name.Contains("Spine") &&

View File

@ -233,7 +233,7 @@ namespace Spine.Unity {
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 (va.Bones == null) {
if (va.Bones == null && va.TimelineAttachment == null) {
var localVerts = va.Vertices;
for (int i = 0; i < bufferTargetSize; i++) {
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.
/// 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>
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
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.
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
/// 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 TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) {
@ -67,5 +98,35 @@ namespace Spine.Unity.AnimationTools {
}
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 () {
AnimationStateTests.logImplementation += Log;
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) {

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) {
if (endAtClipEnd)
if (!isPaused && endAtClipEnd)
HandleClipEnd();
else if (isPaused) // stop event occurred after pause, so resume again
HandleResume(playable);
}
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);
#if UNITY_EDITOR
WarnIfDuplicateTrackIndex();
#endif
var mixerBehaviour = scriptPlayable.GetBehaviour();
mixerBehaviour.trackIndex = this.trackIndex;
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",
"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)",
"version": "4.1.0",
"version": "4.1.2",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.timeline",
"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)",
"version": "4.1.0",
"version": "4.1.2",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",