Merge branch '3.7-beta' into 3.7-beta-cpp
@ -56,7 +56,7 @@ using std::min;
|
||||
using std::max;
|
||||
|
||||
namespace spine {
|
||||
|
||||
|
||||
static Cocos2dTextureLoader textureLoader;
|
||||
|
||||
void SkeletonRenderer::destroyScratchBuffers() {
|
||||
@ -72,7 +72,7 @@ SkeletonRenderer* SkeletonRenderer::createWithSkeleton(Skeleton* skeleton, bool
|
||||
node->autorelease();
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
SkeletonRenderer* SkeletonRenderer::createWithData (SkeletonData* skeletonData, bool ownsSkeletonData) {
|
||||
SkeletonRenderer* node = new SkeletonRenderer(skeletonData, ownsSkeletonData);
|
||||
node->autorelease();
|
||||
@ -96,24 +96,24 @@ void SkeletonRenderer::initialize () {
|
||||
worldVertices = new float[INITIAL_WORLD_VERTICES_LENGTH];
|
||||
worldVerticesLength = INITIAL_WORLD_VERTICES_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
_clipper = new (__FILE__, __LINE__) SkeletonClipping();
|
||||
|
||||
_blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
|
||||
setOpacityModifyRGB(true);
|
||||
|
||||
setupGLProgramState(false);
|
||||
|
||||
|
||||
_skeleton->setToSetupPose();
|
||||
_skeleton->updateWorldTransform();
|
||||
}
|
||||
|
||||
|
||||
void SkeletonRenderer::setupGLProgramState (bool twoColorTintEnabled) {
|
||||
if (twoColorTintEnabled) {
|
||||
setGLProgramState(SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Texture2D *texture = nullptr;
|
||||
for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) {
|
||||
Slot* slot = _skeleton->getDrawOrder()[i];
|
||||
@ -127,7 +127,7 @@ void SkeletonRenderer::setupGLProgramState (bool twoColorTintEnabled) {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (texture != nullptr) {
|
||||
break;
|
||||
}
|
||||
@ -143,7 +143,7 @@ void SkeletonRenderer::setSkeletonData (SkeletonData *skeletonData, bool ownsSke
|
||||
SkeletonRenderer::SkeletonRenderer ()
|
||||
: _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) {
|
||||
}
|
||||
|
||||
|
||||
SkeletonRenderer::SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas)
|
||||
: _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) {
|
||||
initWithSkeleton(skeleton, ownsSkeleton, ownsSkeletonData, ownsAtlas);
|
||||
@ -177,10 +177,10 @@ void SkeletonRenderer::initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, b
|
||||
_ownsSkeleton = ownsSkeleton;
|
||||
_ownsSkeletonData = ownsSkeletonData;
|
||||
_ownsAtlas = ownsAtlas;
|
||||
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
|
||||
void SkeletonRenderer::initWithData (SkeletonData* skeletonData, bool ownsSkeletonData) {
|
||||
_ownsSkeleton = true;
|
||||
setSkeletonData(skeletonData, ownsSkeletonData);
|
||||
@ -221,11 +221,11 @@ void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, co
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
|
||||
void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) {
|
||||
_atlas = atlas;
|
||||
_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
|
||||
|
||||
|
||||
SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(_attachmentLoader);
|
||||
binary->setScale(scale);
|
||||
SkeletonData* skeletonData = binary->readSkeletonDataFile(skeletonDataFile.c_str());
|
||||
@ -233,16 +233,16 @@ void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile,
|
||||
delete binary;
|
||||
_ownsSkeleton = true;
|
||||
setSkeletonData(skeletonData, true);
|
||||
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) {
|
||||
_atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader);
|
||||
CCASSERT(_atlas, "Error reading atlas file.");
|
||||
|
||||
|
||||
_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
|
||||
|
||||
|
||||
SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(_attachmentLoader);
|
||||
binary->setScale(scale);
|
||||
SkeletonData* skeletonData = binary->readSkeletonDataFile(skeletonDataFile.c_str());
|
||||
@ -251,7 +251,7 @@ void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile,
|
||||
_ownsSkeleton = true;
|
||||
_ownsAtlas = true;
|
||||
setSkeletonData(skeletonData, true);
|
||||
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
@ -259,20 +259,25 @@ void SkeletonRenderer::update (float deltaTime) {
|
||||
Node::update(deltaTime);
|
||||
if (_ownsSkeleton) _skeleton->update(deltaTime * _timeScale);
|
||||
}
|
||||
|
||||
|
||||
void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t transformFlags) {
|
||||
SkeletonBatch* batch = SkeletonBatch::getInstance();
|
||||
SkeletonTwoColorBatch* twoColorBatch = SkeletonTwoColorBatch::getInstance();
|
||||
bool isTwoColorTint = this->isTwoColorTint();
|
||||
|
||||
if (_effect) _effect->begin(*_skeleton);
|
||||
|
||||
// Early exit if the skeleton is invisible
|
||||
if (getDisplayedOpacity() == 0 || _skeleton->color.a == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
if (_effect) _effect->begin(_effect, _skeleton);
|
||||
|
||||
Color4F nodeColor;
|
||||
nodeColor.r = getDisplayedColor().r / (float)255;
|
||||
nodeColor.g = getDisplayedColor().g / (float)255;
|
||||
nodeColor.b = getDisplayedColor().b / (float)255;
|
||||
nodeColor.a = getDisplayedOpacity() / (float)255;
|
||||
|
||||
|
||||
Color4F color;
|
||||
Color4F darkColor;
|
||||
float darkPremultipliedAlpha = _premultipliedAlpha ? 255 : 0;
|
||||
@ -281,32 +286,44 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
bool inRange = _startSlotIndex != -1 || _endSlotIndex != -1 ? false : true;
|
||||
for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) {
|
||||
Slot* slot = _skeleton->getDrawOrder()[i];
|
||||
|
||||
|
||||
if (_startSlotIndex >= 0 && _startSlotIndex == slot->getData().getIndex()) {
|
||||
inRange = true;
|
||||
}
|
||||
|
||||
|
||||
if (!inRange) {
|
||||
_clipper->clipEnd(*slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (_endSlotIndex >= 0 && _endSlotIndex == slot->getData().getIndex()) {
|
||||
inRange = false;
|
||||
}
|
||||
|
||||
|
||||
if (!slot->getAttachment()) {
|
||||
_clipper->clipEnd(*slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Early exit if slot is invisible
|
||||
if (slot->color.a == 0) {
|
||||
spSkeletonClipping_clipEnd(_clipper, slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
cocos2d::TrianglesCommand::Triangles triangles;
|
||||
TwoColorTriangles trianglesTwoColor;
|
||||
|
||||
|
||||
if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) {
|
||||
RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment();
|
||||
attachmentVertices = (AttachmentVertices*)attachment->getRendererObject();
|
||||
|
||||
|
||||
// Early exit if attachment is invisible
|
||||
if (attachment->color.a == 0) {
|
||||
spSkeletonClipping_clipEnd(_clipper, slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isTwoColorTint) {
|
||||
triangles.indices = attachmentVertices->_triangles->indices;
|
||||
triangles.indexCount = attachmentVertices->_triangles->indexCount;
|
||||
@ -324,16 +341,22 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
}
|
||||
attachment->computeWorldVertices(slot->getBone(), (float*)trianglesTwoColor.verts, 0, 7);
|
||||
}
|
||||
|
||||
|
||||
color.r = attachment->getColor().r;
|
||||
color.g = attachment->getColor().g;
|
||||
color.b = attachment->getColor().b;
|
||||
color.a = attachment->getColor().a;
|
||||
}
|
||||
else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) {
|
||||
MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment();
|
||||
MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment();
|
||||
attachmentVertices = (AttachmentVertices*)attachment->getRendererObject();
|
||||
|
||||
|
||||
// Early exit if attachment is invisible
|
||||
if (attachment->color.a == 0) {
|
||||
spSkeletonClipping_clipEnd(_clipper, slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isTwoColorTint) {
|
||||
triangles.indices = attachmentVertices->_triangles->indices;
|
||||
triangles.indexCount = attachmentVertices->_triangles->indexCount;
|
||||
@ -351,7 +374,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
}
|
||||
attachment->computeWorldVertices(*slot, 0, attachment->getWorldVerticesLength(), (float*)trianglesTwoColor.verts, 0, 7);
|
||||
}
|
||||
|
||||
|
||||
color.r = attachment->getColor().r;
|
||||
color.g = attachment->getColor().g;
|
||||
color.b = attachment->getColor().b;
|
||||
@ -365,7 +388,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
_clipper->clipEnd(*slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (slot->hasDarkColor()) {
|
||||
darkColor.r = slot->getDarkColor().r * 255;
|
||||
darkColor.g = slot->getDarkColor().g * 255;
|
||||
@ -376,7 +399,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
darkColor.b = 0;
|
||||
}
|
||||
darkColor.a = darkPremultipliedAlpha;
|
||||
|
||||
|
||||
color.a *= nodeColor.a * _skeleton->getColor().a * slot->getColor().a * 255;
|
||||
// skip rendering if the color of this attachment is 0
|
||||
if (color.a == 0){
|
||||
@ -387,7 +410,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
color.r *= nodeColor.r * _skeleton->getColor().r * slot->getColor().r * multiplier;
|
||||
color.g *= nodeColor.g * _skeleton->getColor().g * slot->getColor().g * multiplier;
|
||||
color.b *= nodeColor.b * _skeleton->getColor().b * slot->getColor().b * multiplier;
|
||||
|
||||
|
||||
BlendFunc blendFunc;
|
||||
switch (slot->getData().getBlendMode()) {
|
||||
case BlendMode_Additive:
|
||||
@ -406,25 +429,25 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
blendFunc.src = _premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
|
||||
blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
|
||||
|
||||
if (!isTwoColorTint) {
|
||||
if (_clipper->isClipping()) {
|
||||
_clipper->clipTriangles((float*)&triangles.verts[0].vertices, triangles.indices, triangles.indexCount, (float*)&triangles.verts[0].texCoords, sizeof(cocos2d::V3F_C4B_T2F) / 4);
|
||||
batch->deallocateVertices(triangles.vertCount);
|
||||
|
||||
|
||||
if (_clipper->getClippedTriangles().size() == 0){
|
||||
_clipper->clipEnd(*slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
triangles.vertCount = _clipper->getClippedVertices().size() >> 1;
|
||||
triangles.verts = batch->allocateVertices(triangles.vertCount);
|
||||
triangles.indexCount = _clipper->getClippedTriangles().size();
|
||||
triangles.indices = batch->allocateIndices(triangles.indexCount);
|
||||
memcpy(triangles.indices, _clipper->getClippedTriangles().buffer(), sizeof(unsigned short) * _clipper->getClippedTriangles().size());
|
||||
|
||||
|
||||
cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags);
|
||||
|
||||
|
||||
float* verts = _clipper->getClippedVertices().buffer();
|
||||
float* uvs = _clipper->getClippedUVs().buffer();
|
||||
if (_effect) {
|
||||
@ -464,7 +487,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
}
|
||||
} else {
|
||||
cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags);
|
||||
|
||||
|
||||
if (_effect) {
|
||||
Color light;
|
||||
Color dark;
|
||||
@ -497,23 +520,23 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
if (_clipper->isClipping()) {
|
||||
_clipper->clipTriangles((float*)&trianglesTwoColor.verts[0].position, trianglesTwoColor.indices, trianglesTwoColor.indexCount, (float*)&trianglesTwoColor.verts[0].texCoords, sizeof(V3F_C4B_C4B_T2F) / 4);
|
||||
twoColorBatch->deallocateVertices(trianglesTwoColor.vertCount);
|
||||
|
||||
|
||||
if (_clipper->getClippedTriangles().size() == 0){
|
||||
_clipper->clipEnd(*slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
trianglesTwoColor.vertCount = _clipper->getClippedVertices().size() >> 1;
|
||||
trianglesTwoColor.verts = twoColorBatch->allocateVertices(trianglesTwoColor.vertCount);
|
||||
trianglesTwoColor.indexCount = _clipper->getClippedTriangles().size();
|
||||
trianglesTwoColor.indices = twoColorBatch->allocateIndices(trianglesTwoColor.indexCount);
|
||||
memcpy(trianglesTwoColor.indices, _clipper->getClippedTriangles().buffer(), sizeof(unsigned short) * _clipper->getClippedTriangles().size());
|
||||
|
||||
|
||||
TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags);
|
||||
|
||||
|
||||
float* verts = _clipper->getClippedVertices().buffer();
|
||||
float* uvs = _clipper->getClippedUVs().buffer();
|
||||
|
||||
|
||||
if (_effect) {
|
||||
Color light;
|
||||
Color dark;
|
||||
@ -562,7 +585,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
}
|
||||
} else {
|
||||
TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags);
|
||||
|
||||
|
||||
if (_effect) {
|
||||
Color light;
|
||||
Color dark;
|
||||
@ -574,7 +597,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
dark.g = darkColor.g / 255.0f;
|
||||
dark.b = darkColor.b / 255.0f;
|
||||
dark.a = darkColor.a / 255.0f;
|
||||
|
||||
|
||||
for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) {
|
||||
V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
|
||||
Color lightCopy = light;
|
||||
@ -607,10 +630,10 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
_clipper->clipEnd(*slot);
|
||||
}
|
||||
_clipper->clipEnd();
|
||||
|
||||
|
||||
if (lastTwoColorTrianglesCommand) {
|
||||
Node* parent = this->getParent();
|
||||
|
||||
|
||||
// We need to decide if we can postpone flushing the current
|
||||
// batch. We can postpone if the next sibling node is a
|
||||
// two color tinted skeleton with the same global-z.
|
||||
@ -644,7 +667,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (_effect) _effect->end();
|
||||
|
||||
if (_debugSlots || _debugBones || _debugMeshes) {
|
||||
@ -657,9 +680,9 @@ void SkeletonRenderer::drawDebug (Renderer* renderer, const Mat4 &transform, uin
|
||||
Director* director = Director::getInstance();
|
||||
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
|
||||
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);
|
||||
|
||||
|
||||
DrawNode* drawNode = DrawNode::create();
|
||||
|
||||
|
||||
if (_debugSlots) {
|
||||
// Slots.
|
||||
// DrawPrimitives::setDrawColor4B(0, 0, 255, 255);
|
||||
@ -695,7 +718,7 @@ void SkeletonRenderer::drawDebug (Renderer* renderer, const Mat4 &transform, uin
|
||||
if (i == 0) color = Color4F::GREEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (_debugMeshes) {
|
||||
// Meshes.
|
||||
glLineWidth(1);
|
||||
@ -714,13 +737,13 @@ void SkeletonRenderer::drawDebug (Renderer* renderer, const Mat4 &transform, uin
|
||||
drawNode->drawLine(v3, v1, Color4F::YELLOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
drawNode->draw(renderer, transform, transformFlags);
|
||||
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
|
||||
}
|
||||
|
||||
|
||||
Rect SkeletonRenderer::getBoundingBox () const {
|
||||
float minX = FLT_MAX, minY = FLT_MAX, maxX = -FLT_MAX, maxY = -FLT_MAX;
|
||||
float scaleX = getScaleX(), scaleY = getScaleY();
|
||||
@ -748,7 +771,7 @@ Rect SkeletonRenderer::getBoundingBox () const {
|
||||
}
|
||||
}
|
||||
Vec2 position = getPosition();
|
||||
if (minX == FLT_MAX) minX = minY = maxX = maxY = 0;
|
||||
if (minX == FLT_MAX) minX = minY = maxX = maxY = 0;
|
||||
return Rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
@ -792,7 +815,7 @@ bool SkeletonRenderer::setAttachment (const std::string& slotName, const std::st
|
||||
bool SkeletonRenderer::setAttachment (const std::string& slotName, const char* attachmentName) {
|
||||
return _skeleton->getAttachment(slotName.c_str(), attachmentName) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
void SkeletonRenderer::setTwoColorTint(bool enabled) {
|
||||
setupGLProgramState(enabled);
|
||||
}
|
||||
@ -800,11 +823,11 @@ void SkeletonRenderer::setTwoColorTint(bool enabled) {
|
||||
bool SkeletonRenderer::isTwoColorTint() {
|
||||
return getGLProgramState() == SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState();
|
||||
}
|
||||
|
||||
|
||||
void SkeletonRenderer::setVertexEffect(VertexEffect *effect) {
|
||||
this->_effect = effect;
|
||||
}
|
||||
|
||||
|
||||
void SkeletonRenderer::setSlotsRange(int startSlotIndex, int endSlotIndex) {
|
||||
this->_startSlotIndex = startSlotIndex;
|
||||
this->_endSlotIndex = endSlotIndex;
|
||||
@ -834,7 +857,7 @@ void SkeletonRenderer::setDebugBonesEnabled (bool enabled) {
|
||||
bool SkeletonRenderer::getDebugBonesEnabled () const {
|
||||
return _debugBones;
|
||||
}
|
||||
|
||||
|
||||
void SkeletonRenderer::setDebugMeshesEnabled (bool enabled) {
|
||||
_debugMeshes = enabled;
|
||||
}
|
||||
|
||||
@ -53,10 +53,6 @@ namespace Spine {
|
||||
internal float a, b, worldX;
|
||||
internal float c, d, worldY;
|
||||
|
||||
// internal float worldSignX, worldSignY;
|
||||
// public float WorldSignX { get { return worldSignX; } }
|
||||
// public float WorldSignY { get { return worldSignY; } }
|
||||
|
||||
internal bool sorted;
|
||||
|
||||
public BoneData Data { get { return data; } }
|
||||
@ -152,27 +148,13 @@ namespace Spine {
|
||||
|
||||
Bone parent = this.parent;
|
||||
if (parent == null) { // Root bone.
|
||||
float rotationY = rotation + 90 + shearY;
|
||||
float la = MathUtils.CosDeg(rotation + shearX) * scaleX;
|
||||
float lb = MathUtils.CosDeg(rotationY) * scaleY;
|
||||
float lc = MathUtils.SinDeg(rotation + shearX) * scaleX;
|
||||
float ld = MathUtils.SinDeg(rotationY) * scaleY;
|
||||
if (skeleton.flipX) {
|
||||
x = -x;
|
||||
la = -la;
|
||||
lb = -lb;
|
||||
}
|
||||
if (skeleton.flipY != yDown) {
|
||||
y = -y;
|
||||
lc = -lc;
|
||||
ld = -ld;
|
||||
}
|
||||
a = la;
|
||||
b = lb;
|
||||
c = lc;
|
||||
d = ld;
|
||||
worldX = x + skeleton.x;
|
||||
worldY = y + skeleton.y;
|
||||
float rotationY = rotation + 90 + shearY, sx = skeleton.scaleX, sy = skeleton.scaleY;
|
||||
a = MathUtils.CosDeg(rotation + shearX) * scaleX * sx;
|
||||
b = MathUtils.CosDeg(rotationY) * scaleY * sy;
|
||||
c = MathUtils.SinDeg(rotation + shearX) * scaleX * sx;
|
||||
d = MathUtils.SinDeg(rotationY) * scaleY * sy;
|
||||
worldX = x * sx + skeleton.x;
|
||||
worldY = y * sy + skeleton.y;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -228,8 +210,8 @@ namespace Spine {
|
||||
case TransformMode.NoScale:
|
||||
case TransformMode.NoScaleOrReflection: {
|
||||
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
|
||||
float za = pa * cos + pb * sin;
|
||||
float zc = pc * cos + pd * sin;
|
||||
float za = (pa * cos + pb * sin) / skeleton.scaleX;
|
||||
float zc = (pc * cos + pd * sin) / skeleton.scaleY;
|
||||
float s = (float)Math.Sqrt(za * za + zc * zc);
|
||||
if (s > 0.00001f) s = 1 / s;
|
||||
za *= s;
|
||||
@ -242,26 +224,18 @@ namespace Spine {
|
||||
float lb = MathUtils.CosDeg(90 + shearY) * scaleY;
|
||||
float lc = MathUtils.SinDeg(shearX) * scaleX;
|
||||
float ld = MathUtils.SinDeg(90 + shearY) * scaleY;
|
||||
if (data.transformMode != TransformMode.NoScaleOrReflection? pa * pd - pb* pc< 0 : skeleton.flipX != skeleton.flipY) {
|
||||
zb = -zb;
|
||||
zd = -zd;
|
||||
}
|
||||
a = za * la + zb * lc;
|
||||
b = za * lb + zb * ld;
|
||||
c = zc * la + zd * lc;
|
||||
d = zc * lb + zd * ld;
|
||||
return;
|
||||
d = zc * lb + zd * ld;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeleton.flipX) {
|
||||
a = -a;
|
||||
b = -b;
|
||||
}
|
||||
if (skeleton.flipY != Bone.yDown) {
|
||||
c = -c;
|
||||
d = -d;
|
||||
}
|
||||
a *= skeleton.scaleX;
|
||||
b *= skeleton.scaleX;
|
||||
c *= skeleton.scaleY;
|
||||
d *= skeleton.scaleY;
|
||||
}
|
||||
|
||||
public void SetToSetupPose () {
|
||||
|
||||
@ -45,7 +45,7 @@ namespace Spine {
|
||||
internal Skin skin;
|
||||
internal float r = 1, g = 1, b = 1, a = 1;
|
||||
internal float time;
|
||||
internal bool flipX, flipY;
|
||||
internal float scaleX, scaleY;
|
||||
internal float x, y;
|
||||
|
||||
public SkeletonData Data { get { return data; } }
|
||||
@ -64,8 +64,14 @@ namespace Spine {
|
||||
public float Time { get { return time; } set { time = value; } }
|
||||
public float X { get { return x; } set { x = value; } }
|
||||
public float Y { get { return y; } set { y = value; } }
|
||||
public bool FlipX { get { return flipX; } set { flipX = value; } }
|
||||
public bool FlipY { get { return flipY; } set { flipY = value; } }
|
||||
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||
|
||||
[Obsolete("Use ScaleX instead. FlipX is when ScaleX is negative.")]
|
||||
public bool FlipX { get { return scaleX < 0; } set { scaleX = value ? -1f : 1f; } }
|
||||
|
||||
[Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")]
|
||||
public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } }
|
||||
|
||||
public Bone RootBone {
|
||||
get { return bones.Count == 0 ? null : bones.Items[0]; }
|
||||
|
||||
@ -37,6 +37,7 @@ import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.VertexAttachment;
|
||||
|
||||
@ -1294,11 +1295,12 @@ public class Animation {
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes an IK constraint's {@link IkConstraint#getMix()} and {@link IkConstraint#getBendDirection()}. */
|
||||
/** Changes an IK constraint's {@link IkConstraint#getMix()}, {@link IkConstraint#getBendDirection()}, and
|
||||
* {@link IkConstraint#getStretch()}. */
|
||||
static public class IkConstraintTimeline extends CurveTimeline {
|
||||
static public final int ENTRIES = 3;
|
||||
static private final int PREV_TIME = -3, PREV_MIX = -2, PREV_BEND_DIRECTION = -1;
|
||||
static private final int MIX = 1, BEND_DIRECTION = 2;
|
||||
static public final int ENTRIES = 4;
|
||||
static private final int PREV_TIME = -4, PREV_MIX = -3, PREV_BEND_DIRECTION = -2, PREV_STRETCH = -1;
|
||||
static private final int MIX = 1, BEND_DIRECTION = 2, STRETCH = 3;
|
||||
|
||||
int ikConstraintIndex;
|
||||
private final float[] frames; // time, mix, bendDirection, ...
|
||||
@ -1328,11 +1330,12 @@ public class Animation {
|
||||
}
|
||||
|
||||
/** Sets the time in seconds, mix, and bend direction for the specified key frame. */
|
||||
public void setFrame (int frameIndex, float time, float mix, int bendDirection) {
|
||||
public void setFrame (int frameIndex, float time, float mix, int bendDirection, boolean stretch) {
|
||||
frameIndex *= ENTRIES;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + MIX] = mix;
|
||||
frames[frameIndex + BEND_DIRECTION] = bendDirection;
|
||||
frames[frameIndex + STRETCH] = stretch ? 1 : 0;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, MixBlend blend,
|
||||
@ -1345,10 +1348,12 @@ public class Animation {
|
||||
case setup:
|
||||
constraint.mix = constraint.data.mix;
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.stretch = constraint.data.stretch;
|
||||
return;
|
||||
case first:
|
||||
constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.stretch = constraint.data.stretch;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -1356,11 +1361,19 @@ public class Animation {
|
||||
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
|
||||
if (blend == setup) {
|
||||
constraint.mix = constraint.data.mix + (frames[frames.length + PREV_MIX] - constraint.data.mix) * alpha;
|
||||
constraint.bendDirection = direction == out ? constraint.data.bendDirection
|
||||
: (int)frames[frames.length + PREV_BEND_DIRECTION];
|
||||
if (direction == out) {
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.stretch = constraint.data.stretch;
|
||||
} else {
|
||||
constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
|
||||
constraint.stretch = frames[frames.length + PREV_STRETCH] != 0;
|
||||
}
|
||||
} else {
|
||||
constraint.mix += (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
|
||||
if (direction == in) constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
|
||||
if (direction == in) {
|
||||
constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
|
||||
constraint.stretch = frames[frames.length + PREV_STRETCH] != 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -1373,11 +1386,19 @@ public class Animation {
|
||||
|
||||
if (blend == setup) {
|
||||
constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
|
||||
constraint.bendDirection = direction == out ? constraint.data.bendDirection
|
||||
: (int)frames[frame + PREV_BEND_DIRECTION];
|
||||
if (direction == out) {
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.stretch = constraint.data.stretch;
|
||||
} else {
|
||||
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
|
||||
constraint.stretch = frames[frame + PREV_STRETCH] != 0;
|
||||
}
|
||||
} else {
|
||||
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
|
||||
if (direction == in) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
|
||||
if (direction == in) {
|
||||
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
|
||||
constraint.stretch = frames[frame + PREV_STRETCH] != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,8 +42,9 @@ public class IkConstraint implements Constraint {
|
||||
final IkConstraintData data;
|
||||
final Array<Bone> bones;
|
||||
Bone target;
|
||||
float mix = 1;
|
||||
int bendDirection;
|
||||
boolean stretch;
|
||||
float mix = 1;
|
||||
|
||||
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
@ -51,6 +52,7 @@ public class IkConstraint implements Constraint {
|
||||
this.data = data;
|
||||
mix = data.mix;
|
||||
bendDirection = data.bendDirection;
|
||||
stretch = data.stretch;
|
||||
|
||||
bones = new Array(data.bones.size);
|
||||
for (BoneData boneData : data.bones)
|
||||
@ -69,6 +71,7 @@ public class IkConstraint implements Constraint {
|
||||
target = skeleton.bones.get(constraint.target.data.index);
|
||||
mix = constraint.mix;
|
||||
bendDirection = constraint.bendDirection;
|
||||
stretch = constraint.stretch;
|
||||
}
|
||||
|
||||
/** Applies the constraint to the constrained bones. */
|
||||
@ -81,10 +84,10 @@ public class IkConstraint implements Constraint {
|
||||
Array<Bone> bones = this.bones;
|
||||
switch (bones.size) {
|
||||
case 1:
|
||||
apply(bones.first(), target.worldX, target.worldY, mix);
|
||||
apply(bones.first(), target.worldX, target.worldY, stretch, mix);
|
||||
break;
|
||||
case 2:
|
||||
apply(bones.first(), bones.get(1), target.worldX, target.worldY, bendDirection, mix);
|
||||
apply(bones.first(), bones.get(1), target.worldX, target.worldY, bendDirection, stretch, mix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -125,6 +128,16 @@ public class IkConstraint implements Constraint {
|
||||
this.bendDirection = bendDirection;
|
||||
}
|
||||
|
||||
/** When true, if the target is out of range, the parent bone is scaled on the X axis to reach it. If the parent bone has local
|
||||
* nonuniform scale, stretching is not applied. */
|
||||
public boolean getStretch () {
|
||||
return stretch;
|
||||
}
|
||||
|
||||
public void setStretch (boolean stretch) {
|
||||
this.stretch = stretch;
|
||||
}
|
||||
|
||||
/** The IK constraint's setup pose data. */
|
||||
public IkConstraintData getData () {
|
||||
return data;
|
||||
@ -135,7 +148,7 @@ public class IkConstraint implements Constraint {
|
||||
}
|
||||
|
||||
/** Applies 1 bone IK. The target is specified in the world coordinate system. */
|
||||
static public void apply (Bone bone, float targetX, float targetY, float alpha) {
|
||||
static public void apply (Bone bone, float targetX, float targetY, boolean stretch, float alpha) {
|
||||
if (!bone.appliedValid) bone.updateAppliedTransform();
|
||||
Bone p = bone.parent;
|
||||
float id = 1 / (p.a * p.d - p.b * p.c);
|
||||
@ -146,20 +159,25 @@ public class IkConstraint implements Constraint {
|
||||
if (rotationIK > 180)
|
||||
rotationIK -= 360;
|
||||
else if (rotationIK < -180) rotationIK += 360;
|
||||
bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX,
|
||||
float sx = bone.ascaleX;
|
||||
if (stretch) {
|
||||
float b = bone.data.length * sx, dd = (float)Math.sqrt(tx * tx + ty * ty);
|
||||
if (dd > b && b > 0.0001f) sx *= (dd / b - 1) * alpha + 1;
|
||||
}
|
||||
bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, bone.ascaleY, bone.ashearX,
|
||||
bone.ashearY);
|
||||
}
|
||||
|
||||
/** Applies 2 bone IK. The target is specified in the world coordinate system.
|
||||
* @param child A direct descendant of the parent bone. */
|
||||
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) {
|
||||
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, boolean stretch, float alpha) {
|
||||
if (alpha == 0) {
|
||||
child.updateWorldTransform();
|
||||
return;
|
||||
}
|
||||
if (!parent.appliedValid) parent.updateAppliedTransform();
|
||||
if (!child.appliedValid) child.updateAppliedTransform();
|
||||
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, csx = child.ascaleX;
|
||||
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, sx = psx, psy = parent.ascaleY, csx = child.ascaleX;
|
||||
int os1, os2, s2;
|
||||
if (psx < 0) {
|
||||
psx = -psx;
|
||||
@ -195,7 +213,7 @@ public class IkConstraint implements Constraint {
|
||||
c = pp.c;
|
||||
d = pp.d;
|
||||
float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||
float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
|
||||
float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py, dd = tx * tx + ty * ty;
|
||||
x = cwx - pp.worldX;
|
||||
y = cwy - pp.worldY;
|
||||
float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
|
||||
@ -203,10 +221,13 @@ public class IkConstraint implements Constraint {
|
||||
outer:
|
||||
if (u) {
|
||||
l2 *= psx;
|
||||
float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
|
||||
float cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2);
|
||||
if (cos < -1)
|
||||
cos = -1;
|
||||
else if (cos > 1) cos = 1;
|
||||
else if (cos > 1) {
|
||||
cos = 1;
|
||||
if (stretch && l1 + l2 > 0.0001f) sx *= ((float)Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
|
||||
}
|
||||
a2 = (float)Math.acos(cos) * bendDir;
|
||||
a = l1 + l2 * cos;
|
||||
b = l2 * sin(a2);
|
||||
@ -214,7 +235,7 @@ public class IkConstraint implements Constraint {
|
||||
} else {
|
||||
a = psx * l2;
|
||||
b = psy * l2;
|
||||
float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = atan2(ty, tx);
|
||||
float aa = a * a, bb = b * b, ta = atan2(ty, tx);
|
||||
c = bb * l1 * l1 + aa * dd - aa * bb;
|
||||
float c1 = -2 * bb * l1, c2 = bb - aa;
|
||||
d = c1 * c1 - 4 * c2 * c;
|
||||
@ -266,7 +287,7 @@ public class IkConstraint implements Constraint {
|
||||
if (a1 > 180)
|
||||
a1 -= 360;
|
||||
else if (a1 < -180) a1 += 360;
|
||||
parent.updateWorldTransform(px, py, rotation + a1 * alpha, parent.ascaleX, parent.ascaleY, 0, 0);
|
||||
parent.updateWorldTransform(px, py, rotation + a1 * alpha, sx, parent.ascaleY, 0, 0);
|
||||
rotation = child.arotation;
|
||||
a2 = ((a2 + os) * radDeg - child.ashearX) * s2 + os2 - rotation;
|
||||
if (a2 > 180)
|
||||
|
||||
@ -41,6 +41,7 @@ public class IkConstraintData {
|
||||
final Array<BoneData> bones = new Array();
|
||||
BoneData target;
|
||||
int bendDirection = 1;
|
||||
boolean stretch;
|
||||
float mix = 1;
|
||||
|
||||
public IkConstraintData (String name) {
|
||||
@ -86,6 +87,16 @@ public class IkConstraintData {
|
||||
this.bendDirection = bendDirection;
|
||||
}
|
||||
|
||||
/** When true, if the target is out of range, the parent bone is scaled on the X axis to reach it. If the parent bone has local
|
||||
* nonuniform scale, stretching is not applied. */
|
||||
public boolean getStretch () {
|
||||
return stretch;
|
||||
}
|
||||
|
||||
public void setStretch (boolean stretch) {
|
||||
this.stretch = stretch;
|
||||
}
|
||||
|
||||
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
|
||||
public float getMix () {
|
||||
return mix;
|
||||
|
||||
@ -395,6 +395,7 @@ public class Skeleton {
|
||||
for (int i = 0, n = ikConstraints.size; i < n; i++) {
|
||||
IkConstraint constraint = ikConstraints.get(i);
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
constraint.stretch = constraint.data.stretch;
|
||||
constraint.mix = constraint.data.mix;
|
||||
}
|
||||
|
||||
|
||||
@ -232,6 +232,7 @@ public class SkeletonBinary {
|
||||
data.target = skeletonData.bones.get(input.readInt(true));
|
||||
data.mix = input.readFloat();
|
||||
data.bendDirection = input.readByte();
|
||||
data.stretch = input.readBoolean();
|
||||
skeletonData.ikConstraints.add(data);
|
||||
}
|
||||
|
||||
@ -660,7 +661,7 @@ public class SkeletonBinary {
|
||||
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
|
||||
timeline.ikConstraintIndex = index;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte());
|
||||
timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte(), input.readBoolean());
|
||||
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
|
||||
}
|
||||
timelines.add(timeline);
|
||||
|
||||
@ -186,6 +186,7 @@ public class SkeletonJson {
|
||||
if (data.target == null) throw new SerializationException("IK target bone not found: " + targetName);
|
||||
|
||||
data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1;
|
||||
data.stretch = constraintMap.getBoolean("stretch", false);
|
||||
data.mix = constraintMap.getFloat("mix", 1);
|
||||
|
||||
skeletonData.ikConstraints.add(data);
|
||||
@ -568,7 +569,7 @@ public class SkeletonJson {
|
||||
int frameIndex = 0;
|
||||
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
|
||||
timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("mix", 1),
|
||||
valueMap.getBoolean("bendPositive", true) ? 1 : -1);
|
||||
valueMap.getBoolean("bendPositive", true) ? 1 : -1, valueMap.getBoolean("stretch", false));
|
||||
readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ public class SkeletonRenderer {
|
||||
* skeleton is rendered without two color tinting and any mesh attachments will throw an exception.
|
||||
* <p>
|
||||
* This method may change the batch's {@link Batch#setBlendFunctionSeparate(int, int, int, int) blending function}. The
|
||||
* previous blend function is not restore, since that could result in unnecessary flushes, depending on what is rendered
|
||||
* previous blend function is not restored, since that could result in unnecessary flushes, depending on what is rendered
|
||||
* next. */
|
||||
public void draw (Batch batch, Skeleton skeleton) {
|
||||
if (batch instanceof PolygonSpriteBatch) {
|
||||
@ -144,7 +144,7 @@ public class SkeletonRenderer {
|
||||
/** Renders the specified skeleton, including meshes, but without two color tinting.
|
||||
* <p>
|
||||
* This method may change the batch's {@link Batch#setBlendFunctionSeparate(int, int, int, int) blending function}. The
|
||||
* previous blend function is not restore, since that could result in unnecessary flushes, depending on what is rendered
|
||||
* previous blend function is not restored, since that could result in unnecessary flushes, depending on what is rendered
|
||||
* next. */
|
||||
@SuppressWarnings("null")
|
||||
public void draw (PolygonSpriteBatch batch, Skeleton skeleton) {
|
||||
@ -266,7 +266,7 @@ public class SkeletonRenderer {
|
||||
/** Renders the specified skeleton, including meshes and two color tinting.
|
||||
* <p>
|
||||
* This method may change the batch's {@link Batch#setBlendFunctionSeparate(int, int, int, int) blending function}. The
|
||||
* previous blend function is not restore, since that could result in unnecessary flushes, depending on what is rendered
|
||||
* previous blend function is not restored, since that could result in unnecessary flushes, depending on what is rendered
|
||||
* next. */
|
||||
@SuppressWarnings("null")
|
||||
public void draw (TwoColorPolygonBatch batch, Skeleton skeleton) {
|
||||
|
||||
@ -116,6 +116,9 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
|
||||
states.texture = 0;
|
||||
unsigned short quadIndices[6] = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
// Early out if skeleton is invisible
|
||||
if (skeleton->color.a == 0) return;
|
||||
|
||||
if (vertexEffect != 0) vertexEffect->begin(vertexEffect, skeleton);
|
||||
|
||||
sf::Vertex vertex;
|
||||
@ -125,6 +128,12 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
|
||||
Attachment* attachment = slot->attachment;
|
||||
if (!attachment) continue;
|
||||
|
||||
// Early out if slot is invisible
|
||||
if (slot->color.a == 0) {
|
||||
spSkeletonClipping_clipEnd(clipper, slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
float* vertices = worldVertices;
|
||||
int verticesCount = 0;
|
||||
float* uvs = 0;
|
||||
@ -134,16 +143,31 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
|
||||
|
||||
if (attachment->type == ATTACHMENT_REGION) {
|
||||
RegionAttachment* regionAttachment = (RegionAttachment*)attachment;
|
||||
attachmentColor = ®ionAttachment->color;
|
||||
|
||||
// Early out if slot is invisible
|
||||
if (attachmentColor->a == 0) {
|
||||
spSkeletonClipping_clipEnd(clipper, slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
spRegionAttachment_computeWorldVertices(regionAttachment, slot->bone, vertices, 0, 2);
|
||||
verticesCount = 4;
|
||||
uvs = regionAttachment->uvs;
|
||||
indices = quadIndices;
|
||||
indicesCount = 6;
|
||||
texture = (Texture*)((AtlasRegion*)regionAttachment->rendererObject)->page->rendererObject;
|
||||
attachmentColor = ®ionAttachment->color;
|
||||
|
||||
} else if (attachment->type == ATTACHMENT_MESH) {
|
||||
MeshAttachment* mesh = (MeshAttachment*)attachment;
|
||||
attachmentColor = &mesh->color;
|
||||
|
||||
// Early out if slot is invisible
|
||||
if (attachmentColor->a == 0) {
|
||||
spSkeletonClipping_clipEnd(clipper, slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mesh->super.worldVerticesLength > SPINE_MESH_VERTEX_COUNT_MAX) continue;
|
||||
texture = (Texture*)((AtlasRegion*)mesh->rendererObject)->page->rendererObject;
|
||||
spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, worldVertices, 0, 2);
|
||||
@ -151,7 +175,7 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
|
||||
uvs = mesh->uvs;
|
||||
indices = mesh->triangles;
|
||||
indicesCount = mesh->trianglesCount;
|
||||
attachmentColor = &mesh->color;
|
||||
|
||||
} else if (attachment->type == SP_ATTACHMENT_CLIPPING) {
|
||||
spClippingAttachment* clip = (spClippingAttachment*)slot->attachment;
|
||||
spSkeletonClipping_clipStart(clipper, slot, clip);
|
||||
|
||||
@ -91,6 +91,9 @@ void SkeletonDrawable::draw(RenderTarget &target, RenderStates states) const {
|
||||
vertexArray->clear();
|
||||
states.texture = NULL;
|
||||
|
||||
// Early out if the skeleton alpha is 0
|
||||
if (skeleton->getColor().a == 0) return;
|
||||
|
||||
if (vertexEffect != NULL) vertexEffect->begin(*skeleton);
|
||||
|
||||
sf::Vertex vertex;
|
||||
@ -100,6 +103,12 @@ void SkeletonDrawable::draw(RenderTarget &target, RenderStates states) const {
|
||||
Attachment *attachment = slot.getAttachment();
|
||||
if (!attachment) continue;
|
||||
|
||||
// Early out if the slot color is 0
|
||||
if (slot.getColor().a == 0) {
|
||||
clipper.clipEnd(slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<float> *vertices = &worldVertices;
|
||||
int verticesCount = 0;
|
||||
Vector<float> *uvs = NULL;
|
||||
@ -109,17 +118,32 @@ void SkeletonDrawable::draw(RenderTarget &target, RenderStates states) const {
|
||||
|
||||
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
|
||||
RegionAttachment *regionAttachment = (RegionAttachment *) attachment;
|
||||
attachmentColor = ®ionAttachment->getColor();
|
||||
|
||||
// Early out if the attachment color is 0
|
||||
if (attachmentColor->a == 0) {
|
||||
clipper.clipEnd(slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
worldVertices.setSize(8, 0);
|
||||
regionAttachment->computeWorldVertices(slot.getBone(), worldVertices, 0, 2);
|
||||
verticesCount = 4;
|
||||
uvs = ®ionAttachment->getUVs();
|
||||
indices = &quadIndices;
|
||||
indicesCount = 6;
|
||||
texture = (Texture *) ((AtlasRegion *) regionAttachment->getRendererObject())->page->getRendererObject();
|
||||
attachmentColor = ®ionAttachment->getColor();
|
||||
texture = (Texture *) ((AtlasRegion *) regionAttachment->getRendererObject())->page->rendererObject;
|
||||
|
||||
} else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
|
||||
MeshAttachment *mesh = (MeshAttachment *) attachment;
|
||||
attachmentColor = &mesh->getColor();
|
||||
|
||||
// Early out if the attachment color is 0
|
||||
if (attachmentColor->a == 0) {
|
||||
clipper.clipEnd(slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
worldVertices.setSize(mesh->getWorldVerticesLength(), 0);
|
||||
texture = (Texture *) ((AtlasRegion *) mesh->getRendererObject())->page->getRendererObject();
|
||||
mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices, 0, 2);
|
||||
@ -127,7 +151,6 @@ void SkeletonDrawable::draw(RenderTarget &target, RenderStates states) const {
|
||||
uvs = &mesh->getUVs();
|
||||
indices = &mesh->getTriangles();
|
||||
indicesCount = mesh->getTriangles().size();
|
||||
attachmentColor = &mesh->getColor();
|
||||
} else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) {
|
||||
ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment();
|
||||
clipper.clipStart(slot, clip);
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 173cd2c662ebd674f994bff2385cfbf6
|
||||
folderAsset: yes
|
||||
timeCreated: 1529972040
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b804088948820194cbda76af39c08174
|
||||
timeCreated: 1529972058
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,117 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class SpineAnimationTesterTool : MonoBehaviour, IHasSkeletonDataAsset, IHasSkeletonComponent {
|
||||
|
||||
public SkeletonAnimation skeletonAnimation;
|
||||
public SkeletonDataAsset SkeletonDataAsset { get { return skeletonAnimation.SkeletonDataAsset; } }
|
||||
public ISkeletonComponent SkeletonComponent { get { return skeletonAnimation; } }
|
||||
|
||||
public bool useOverrideMixDuration;
|
||||
public float overrideMixDuration = 0.2f;
|
||||
|
||||
[System.Serializable]
|
||||
public struct AnimationControl {
|
||||
[SpineAnimation]
|
||||
public string animationName;
|
||||
public bool loop;
|
||||
public KeyCode key;
|
||||
|
||||
[Space]
|
||||
public bool useCustomMixDuration;
|
||||
public float mixDuration;
|
||||
//public bool useChainToControl;
|
||||
//public int chainToControl;
|
||||
}
|
||||
[System.Serializable]
|
||||
public class ControlledTrack {
|
||||
public List<AnimationControl> controls = new List<AnimationControl>();
|
||||
}
|
||||
|
||||
[Space]
|
||||
public List<ControlledTrack> trackControls = new List<ControlledTrack>();
|
||||
|
||||
[Header("UI")]
|
||||
public UnityEngine.UI.Text boundAnimationsText;
|
||||
public UnityEngine.UI.Text skeletonNameText;
|
||||
|
||||
void OnValidate () {
|
||||
// Fill in the SkeletonData asset name
|
||||
if (skeletonNameText != null) {
|
||||
if (skeletonAnimation != null && skeletonAnimation.skeletonDataAsset != null) {
|
||||
skeletonNameText.text = SkeletonDataAsset.name.Replace("_SkeletonData", "");
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the control list.
|
||||
if (boundAnimationsText != null) {
|
||||
var boundAnimationsStringBuilder = new StringBuilder();
|
||||
boundAnimationsStringBuilder.AppendLine("Animation Controls:");
|
||||
|
||||
for (int trackIndex = 0; trackIndex < trackControls.Count; trackIndex++) {
|
||||
|
||||
if (trackIndex > 0)
|
||||
boundAnimationsStringBuilder.AppendLine();
|
||||
|
||||
boundAnimationsStringBuilder.AppendFormat("---- Track {0} ---- \n", trackIndex);
|
||||
foreach (var ba in trackControls[trackIndex].controls) {
|
||||
string animationName = ba.animationName;
|
||||
if (string.IsNullOrEmpty(animationName))
|
||||
animationName = "SetEmptyAnimation";
|
||||
|
||||
boundAnimationsStringBuilder.AppendFormat("[{0}] {1}\n", ba.key.ToString(), animationName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
boundAnimationsText.text = boundAnimationsStringBuilder.ToString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Start () {
|
||||
if (useOverrideMixDuration) {
|
||||
skeletonAnimation.AnimationState.Data.DefaultMix = overrideMixDuration;
|
||||
}
|
||||
}
|
||||
|
||||
void Update () {
|
||||
var animationState = skeletonAnimation.AnimationState;
|
||||
|
||||
// For each track
|
||||
for (int trackIndex = 0; trackIndex < trackControls.Count; trackIndex++) {
|
||||
|
||||
// For each control in the track
|
||||
foreach (var control in trackControls[trackIndex].controls) {
|
||||
|
||||
// Check each control, and play the appropriate animation.
|
||||
if (Input.GetKeyDown(control.key)) {
|
||||
if (!string.IsNullOrEmpty(control.animationName)) {
|
||||
var trackEntry = animationState.SetAnimation(trackIndex, control.animationName, control.loop);
|
||||
if (control.useCustomMixDuration)
|
||||
trackEntry.MixDuration = control.mixDuration;
|
||||
|
||||
} else {
|
||||
float mix = control.useCustomMixDuration ? control.mixDuration : animationState.Data.DefaultMix;
|
||||
animationState.SetEmptyAnimation(trackIndex, mix);
|
||||
}
|
||||
|
||||
// Don't parse more than one animation per track.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b99b2d8e59226fa4db070f241259fd98
|
||||
timeCreated: 1529972356
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4766fcfd6167d2e46aad772ce3bc898c
|
||||
folderAsset: yes
|
||||
timeCreated: 1531292725
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afd3c9ec31200bc49b169c22f00b010b
|
||||
timeCreated: 1531300871
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cec34498f2eb26b488452ec274c54439
|
||||
timeCreated: 1531292741
|
||||
licenseType: Free
|
||||
NativeFormatImporter:
|
||||
mainObjectFileID: 9100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,62 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
|
||||
public class AnimationStateMecanimState : StateMachineBehaviour {
|
||||
|
||||
#region Inspector
|
||||
public AnimationReferenceAsset animation;
|
||||
|
||||
[System.Serializable]
|
||||
public struct AnimationTransition {
|
||||
public AnimationReferenceAsset from;
|
||||
public AnimationReferenceAsset transition;
|
||||
}
|
||||
|
||||
[UnityEngine.Serialization.FormerlySerializedAs("transitions")]
|
||||
public List<AnimationTransition> fromTransitions = new List<AnimationTransition>();
|
||||
#endregion
|
||||
|
||||
Spine.AnimationState state;
|
||||
|
||||
public void Initialize (Animator animator) {
|
||||
if (state == null) {
|
||||
var animationStateComponent = (animator.GetComponent(typeof(IAnimationStateComponent))) as IAnimationStateComponent;
|
||||
state = animationStateComponent.AnimationState;
|
||||
}
|
||||
}
|
||||
|
||||
override public void OnStateEnter (Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
|
||||
if (state == null) {
|
||||
Initialize(animator);
|
||||
}
|
||||
|
||||
float timeScale = stateInfo.speed;
|
||||
var current = state.GetCurrent(layerIndex);
|
||||
|
||||
bool transitionPlayed = false;
|
||||
if (current != null && fromTransitions.Count > 0) {
|
||||
foreach (var t in fromTransitions) {
|
||||
if (t.from.Animation == current.Animation) {
|
||||
var transitionEntry = state.SetAnimation(layerIndex, t.transition.Animation, false);
|
||||
transitionEntry.TimeScale = timeScale;
|
||||
transitionPlayed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TrackEntry trackEntry;
|
||||
if (transitionPlayed) {
|
||||
trackEntry = state.AddAnimation(layerIndex, animation.Animation, stateInfo.loop, 0);
|
||||
} else {
|
||||
trackEntry = state.SetAnimation(layerIndex, animation.Animation, stateInfo.loop);
|
||||
}
|
||||
trackEntry.TimeScale = timeScale;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 536bdde8dc7bbb641b17da9221d6562f
|
||||
timeCreated: 1531293563
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,82 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
|
||||
namespace Spine.Unity.Examples {
|
||||
public class AnimationStateWithMecanimExample : MonoBehaviour {
|
||||
|
||||
SkeletonAnimation skeletonAnimation;
|
||||
Animator logicAnimator;
|
||||
|
||||
[Header("Controls")]
|
||||
public KeyCode walkButton = KeyCode.LeftShift;
|
||||
public KeyCode jumpButton = KeyCode.Space;
|
||||
|
||||
[Header("Animator Properties")]
|
||||
public string horizontalSpeedProperty = "Speed";
|
||||
public string verticalSpeedProperty = "VerticalSpeed";
|
||||
public string groundedProperty = "Grounded";
|
||||
|
||||
[Header("Fake Physics")]
|
||||
public float jumpDuration = 1.5f;
|
||||
public Vector2 speed;
|
||||
public bool isGrounded;
|
||||
|
||||
void Awake () {
|
||||
skeletonAnimation = GetComponent<SkeletonAnimation>();
|
||||
logicAnimator = GetComponent<Animator>();
|
||||
|
||||
isGrounded = true;
|
||||
}
|
||||
|
||||
void Update () {
|
||||
float x = Input.GetAxisRaw("Horizontal");
|
||||
if (Input.GetKey(walkButton)) {
|
||||
x *= 0.4f;
|
||||
}
|
||||
|
||||
speed.x = x;
|
||||
|
||||
// Flip skeleton.
|
||||
if (x != 0) {
|
||||
skeletonAnimation.Skeleton.ScaleX = x > 0 ? 1f : -1f;
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(jumpButton)) {
|
||||
if (isGrounded)
|
||||
StartCoroutine(FakeJump());
|
||||
}
|
||||
|
||||
logicAnimator.SetFloat(horizontalSpeedProperty, Mathf.Abs(speed.x));
|
||||
logicAnimator.SetFloat(verticalSpeedProperty, speed.y);
|
||||
logicAnimator.SetBool(groundedProperty, isGrounded);
|
||||
|
||||
}
|
||||
|
||||
IEnumerator FakeJump () {
|
||||
// Rise
|
||||
isGrounded = false;
|
||||
speed.y = 10f;
|
||||
float durationLeft = jumpDuration * 0.5f;
|
||||
while (durationLeft > 0) {
|
||||
durationLeft -= Time.deltaTime;
|
||||
if (!Input.GetKey(jumpButton)) break;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Fall
|
||||
speed.y = -10f;
|
||||
float fallDuration = (jumpDuration * 0.5f) - durationLeft;
|
||||
yield return new WaitForSeconds(fallDuration);
|
||||
|
||||
// Land
|
||||
speed.y = 0f;
|
||||
isGrounded = true;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 782062825deffd64ba7e7e9f978788e5
|
||||
timeCreated: 1531300740
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -80,11 +80,11 @@ namespace Spine.Unity.Examples {
|
||||
} else {
|
||||
if (Input.GetKey(rightKey)) {
|
||||
skeletonAnimation.AnimationName = moveAnimation;
|
||||
skeletonAnimation.Skeleton.FlipX = false;
|
||||
skeletonAnimation.Skeleton.ScaleX = 1;
|
||||
transform.Translate(moveSpeed * Time.deltaTime, 0, 0);
|
||||
} else if(Input.GetKey(leftKey)) {
|
||||
skeletonAnimation.AnimationName = moveAnimation;
|
||||
skeletonAnimation.Skeleton.FlipX = true;
|
||||
skeletonAnimation.Skeleton.ScaleX = -1;
|
||||
transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
|
||||
} else {
|
||||
skeletonAnimation.AnimationName = idleAnimation;
|
||||
|
||||
@ -183,7 +183,7 @@ namespace Spine.Unity.Examples {
|
||||
|
||||
// Face intended direction.
|
||||
if (input.x != 0)
|
||||
skeletonAnimation.Skeleton.FlipX = input.x < 0;
|
||||
skeletonAnimation.Skeleton.ScaleX = Mathf.Sign(input.x);
|
||||
|
||||
|
||||
// Effects
|
||||
|
||||
@ -93,11 +93,11 @@ namespace Spine.Unity.Examples {
|
||||
spineAnimationState.AddAnimation(0, idleAnimationName, true, 0);
|
||||
yield return new WaitForSeconds(1f);
|
||||
|
||||
skeleton.FlipX = true; // skeleton allows you to flip the skeleton.
|
||||
skeleton.ScaleX = -1; // skeleton allows you to flip the skeleton.
|
||||
spineAnimationState.SetAnimation(0, idleTurnAnimationName, false);
|
||||
spineAnimationState.AddAnimation(0, idleAnimationName, true, 0);
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
skeleton.FlipX = false;
|
||||
skeleton.ScaleX = 1;
|
||||
spineAnimationState.SetAnimation(0, idleTurnAnimationName, false);
|
||||
spineAnimationState.AddAnimation(0, idleAnimationName, true, 0);
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
|
||||
@ -69,7 +69,7 @@ namespace Spine.Unity.Examples {
|
||||
if (skeletonAnimation == null) return;
|
||||
if (model == null) return;
|
||||
|
||||
if (skeletonAnimation.skeleton.FlipX != model.facingLeft) { // Detect changes in model.facingLeft
|
||||
if ((skeletonAnimation.skeleton.ScaleX < 0) != model.facingLeft) { // Detect changes in model.facingLeft
|
||||
Turn(model.facingLeft);
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ namespace Spine.Unity.Examples {
|
||||
}
|
||||
|
||||
public void Turn (bool facingLeft) {
|
||||
skeletonAnimation.Skeleton.FlipX = facingLeft;
|
||||
skeletonAnimation.Skeleton.ScaleX = facingLeft ? -1f : 1f;
|
||||
// Maybe play a transient turning animation too, then call ChangeStableAnimation.
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -23,7 +23,7 @@ namespace Spine.Unity.Examples {
|
||||
var mousePosition = Input.mousePosition;
|
||||
var worldMousePosition = camera.ScreenToWorldPoint(mousePosition);
|
||||
var skeletonSpacePoint = skeletonAnimation.transform.InverseTransformPoint(worldMousePosition);
|
||||
if (skeletonAnimation.Skeleton.FlipX) skeletonSpacePoint.x *= -1;
|
||||
//if (skeletonAnimation.Skeleton.FlipX) skeletonSpacePoint.x *= -1;
|
||||
bone.SetPosition(skeletonSpacePoint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,8 +69,8 @@ namespace Spine.Unity {
|
||||
sa.initialFlipX = this.initialFlipX;
|
||||
sa.initialFlipY = this.initialFlipY;
|
||||
var skeleton = sa.skeleton;
|
||||
skeleton.FlipX = this.initialFlipX;
|
||||
skeleton.FlipY = this.initialFlipY;
|
||||
skeleton.ScaleX = this.initialFlipX ? 1 : -1;
|
||||
skeleton.ScaleY = this.initialFlipY ? 1 : -1;
|
||||
|
||||
sa.Initialize(false);
|
||||
skeletonAnimations.Add(sa);
|
||||
|
||||
|
Before Width: | Height: | Size: 593 KiB After Width: | Height: | Size: 593 KiB |
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |