Merge branch '3.7-beta' into 3.7-beta-cpp

This commit is contained in:
badlogic 2018-08-24 11:37:12 +02:00
commit f5cc2933be
42 changed files with 11066 additions and 10839 deletions

View File

@ -452,7 +452,7 @@ typedef struct spIkConstraintTimeline {
SP_API spIkConstraintTimeline* spIkConstraintTimeline_create (int framesCount);
SP_API void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, int bendDirection, int /**boolean**/ stretch);
SP_API void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, int bendDirection, int /*boolean*/ compress, int /**boolean**/ stretch);
#ifdef SPINE_SHORT_NAMES
typedef spIkConstraintTimeline IkConstraintTimeline;

View File

@ -49,6 +49,7 @@ typedef struct spIkConstraint {
spBone* target;
int bendDirection;
int /*boolean*/ compress;
int /*boolean*/ stretch;
float mix;
@ -70,7 +71,7 @@ SP_API void spIkConstraint_dispose (spIkConstraint* self);
SP_API void spIkConstraint_apply (spIkConstraint* self);
SP_API void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, int /*boolean*/ stretch, float alpha);
SP_API void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, int /*boolean*/ compress, int /*boolean*/ stretch, int /*boolean*/ uniform, float alpha);
SP_API void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDirection, int /*boolean*/ stretch, float alpha);
#ifdef SPINE_SHORT_NAMES

View File

@ -46,7 +46,9 @@ typedef struct spIkConstraintData {
spBoneData* target;
int bendDirection;
int /*boolean*/ compress;
int /*boolean*/ stretch;
int /*boolean*/ uniform;
float mix;
#ifdef __cplusplus
@ -56,7 +58,9 @@ typedef struct spIkConstraintData {
bones(0),
target(0),
bendDirection(0),
compress(0),
stretch(0),
uniform(0),
mix(0) {
}
#endif

View File

@ -1282,8 +1282,8 @@ void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIndex, fl
/**/
static const int IKCONSTRAINT_PREV_TIME = -4, IKCONSTRAINT_PREV_MIX = -3, IKCONSTRAINT_PREV_BEND_DIRECTION = -2, IKCONSTRAINT_PREV_STRETCH = -1;
static const int IKCONSTRAINT_MIX = 1, IKCONSTRAINT_BEND_DIRECTION = 2, IKCONSTRAINT_STRETCH = 3;
static const int IKCONSTRAINT_PREV_TIME = -5, IKCONSTRAINT_PREV_MIX = -4, IKCONSTRAINT_PREV_BEND_DIRECTION = -3, IKCONSTRAINT_PREV_COMPRESS = -2, IKCONSTRAINT_PREV_STRETCH = -1;
static const int IKCONSTRAINT_MIX = 1, IKCONSTRAINT_BEND_DIRECTION = 2, IKCONSTRAINT_COMPRESS = 3, IKCONSTRAINT_STRETCH = 4;
void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction) {
@ -1301,11 +1301,13 @@ void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skel
case SP_MIX_BLEND_SETUP:
constraint->mix = constraint->data->mix;
constraint->bendDirection = constraint->data->bendDirection;
constraint->compress = constraint->data->compress;
constraint->stretch = constraint->data->stretch;
return;
case SP_MIX_BLEND_FIRST:
constraint->mix += (constraint->data->mix - constraint->mix) * alpha;
constraint->bendDirection = constraint->data->bendDirection;
constraint->compress = constraint->data->compress;
constraint->stretch = constraint->data->stretch;
case SP_MIX_BLEND_REPLACE:
case SP_MIX_BLEND_ADD:
@ -1321,15 +1323,18 @@ void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skel
constraint->mix = constraint->data->mix + (frames[framesCount + IKCONSTRAINT_PREV_MIX] - constraint->data->mix) * alpha;
if (direction == SP_MIX_DIRECTION_OUT) {
constraint->bendDirection = constraint->data->bendDirection;
constraint->compress = constraint->data->compress;
constraint->stretch = constraint->data->stretch;
} else {
constraint->bendDirection = (int)frames[framesCount + IKCONSTRAINT_PREV_BEND_DIRECTION];
constraint->compress = frames[framesCount + IKCONSTRAINT_PREV_COMPRESS] ? 1 : 0;
constraint->stretch = frames[framesCount + IKCONSTRAINT_PREV_STRETCH] ? 1 : 0;
}
} else {
constraint->mix += (frames[framesCount + IKCONSTRAINT_PREV_MIX] - constraint->mix) * alpha;
if (direction == SP_MIX_DIRECTION_IN) {
constraint->bendDirection = (int)frames[framesCount + IKCONSTRAINT_PREV_BEND_DIRECTION];
constraint->compress = frames[framesCount + IKCONSTRAINT_PREV_COMPRESS] ? 1 : 0;
constraint->stretch = frames[framesCount + IKCONSTRAINT_PREV_STRETCH] ? 1 : 0;
}
}
@ -1346,15 +1351,18 @@ void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skel
constraint->mix = constraint->data->mix + (mix + (frames[frame + IKCONSTRAINT_MIX] - mix) * percent - constraint->data->mix) * alpha;
if (direction == SP_MIX_DIRECTION_OUT) {
constraint->bendDirection = constraint->data->bendDirection;
constraint->compress = constraint->data->compress;
constraint->stretch = constraint->data->stretch;
} else {
constraint->bendDirection = (int)frames[frame + IKCONSTRAINT_PREV_BEND_DIRECTION];
constraint->compress = frames[frame + IKCONSTRAINT_PREV_COMPRESS] ? 1 : 0;
constraint->stretch = frames[frame + IKCONSTRAINT_PREV_STRETCH] ? 1 : 0;
}
} else {
constraint->mix += (mix + (frames[frame + IKCONSTRAINT_MIX] - mix) * percent - constraint->mix) * alpha;
if (direction == SP_MIX_DIRECTION_IN) {
constraint->bendDirection = (int)frames[frame + IKCONSTRAINT_PREV_BEND_DIRECTION];
constraint->compress = frames[frame + IKCONSTRAINT_PREV_COMPRESS] ? 1 : 0;
constraint->stretch = frames[frame + IKCONSTRAINT_PREV_STRETCH] ? 1 : 0;
}
}
@ -1372,11 +1380,12 @@ spIkConstraintTimeline* spIkConstraintTimeline_create (int framesCount) {
return (spIkConstraintTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_IKCONSTRAINT, IKCONSTRAINT_ENTRIES, _spIkConstraintTimeline_apply, _spIkConstraintTimeline_getPropertyId);
}
void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, int bendDirection, int /*boolean*/ stretch) {
void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, int bendDirection, int /*boolean*/ compress, int /*boolean*/ stretch) {
frameIndex *= IKCONSTRAINT_ENTRIES;
self->frames[frameIndex] = time;
self->frames[frameIndex + IKCONSTRAINT_MIX] = mix;
self->frames[frameIndex + IKCONSTRAINT_BEND_DIRECTION] = (float)bendDirection;
self->frames[frameIndex + IKCONSTRAINT_COMPRESS] = compress ? 1 : 0;
self->frames[frameIndex + IKCONSTRAINT_STRETCH] = stretch ? 1 : 0;
}

View File

@ -554,11 +554,11 @@ void _spAnimationState_applyRotateTimeline (spAnimationState* self, spTimeline*
/* Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. */
r1 = blend == SP_MIX_BLEND_SETUP ? bone->data->rotation : bone->rotation;
diff = r2 - r1;
diff -= (16384 - (int)(16384.499999999996 - diff / 360)) * 360;
if (diff == 0) {
total = timelinesRotation[i];
} else {
float lastTotal, lastDiff;
diff -= (16384 - (int)(16384.499999999996 - diff / 360)) * 360;
if (firstFrame) {
lastTotal = 0;
lastDiff = diff;

View File

@ -39,6 +39,7 @@ spIkConstraint *spIkConstraint_create(spIkConstraintData *data, const spSkeleton
spIkConstraint *self = NEW(spIkConstraint);
CONST_CAST(spIkConstraintData*, self->data) = data;
self->bendDirection = data->bendDirection;
self->compress = data->compress;
self->stretch = data->stretch;
self->mix = data->mix;
@ -59,7 +60,7 @@ void spIkConstraint_dispose(spIkConstraint *self) {
void spIkConstraint_apply(spIkConstraint *self) {
switch (self->bonesCount) {
case 1:
spIkConstraint_apply1(self->bones[0], self->target->worldX, self->target->worldY, self->stretch, self->mix);
spIkConstraint_apply1(self->bones[0], self->target->worldX, self->target->worldY, self->compress, self->stretch, self->data->uniform, self->mix);
break;
case 2:
spIkConstraint_apply2(self->bones[0], self->bones[1], self->target->worldX, self->target->worldY, self->bendDirection, self->stretch, self->mix);
@ -67,9 +68,9 @@ void spIkConstraint_apply(spIkConstraint *self) {
}
}
void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, int /*boolean*/ stretch, float alpha) {
void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, int /*boolean*/ compress, int /*boolean*/ stretch, int /*boolean*/ uniform, float alpha) {
spBone* p = bone->parent;
float id, x, y, tx, ty, rotationIK, sx;
float id, x, y, tx, ty, rotationIK, sx, sy, s;
if (!bone->appliedValid) spBone_updateAppliedTransform(bone);
id = 1 / (p->a * p->d - p->b * p->c);
x = targetX - p->worldX, y = targetY - p->worldY;
@ -79,12 +80,17 @@ void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, int /*bo
if (rotationIK > 180) rotationIK -= 360;
else if (rotationIK < -180) rotationIK += 360;
sx = bone->ascaleX;
if (stretch) {
sy = bone->ascaleY;
if (compress || stretch) {
float b = bone->data->length * sx, dd = SQRT(tx * tx + ty * ty);
if (dd > b && b > 0.0001f) sx *= (dd / b - 1) * alpha - 1;
if ((compress && dd < b) || (stretch && dd > b) && (b > 0.0001f)) {
s = (dd / b - 1) * alpha + 1;
sx *= s;
if (uniform) sy *= s;
}
}
spBone_updateWorldTransformWith(bone, bone->ax, bone->ay, bone->arotation + rotationIK * alpha, sx,
bone->ascaleY, bone->ashearX, bone->ashearY);
sy, bone->ashearX, bone->ashearY);
}
void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDir, int /*boolean*/ stretch, float alpha) {

View File

@ -35,7 +35,9 @@ spIkConstraintData* spIkConstraintData_create (const char* name) {
spIkConstraintData* self = NEW(spIkConstraintData);
MALLOC_STR(self->name, name);
self->bendDirection = 1;
self->compress = 0;
self->stretch = 0;
self->uniform = 0;
self->mix = 1;
return self;
}

View File

@ -436,6 +436,7 @@ void spSkeleton_setBonesToSetupPose (const spSkeleton* self) {
for (i = 0; i < self->ikConstraintsCount; ++i) {
spIkConstraint* ikConstraint = self->ikConstraints[i];
ikConstraint->bendDirection = ikConstraint->data->bendDirection;
ikConstraint->compress = ikConstraint->data->compress;
ikConstraint->stretch = ikConstraint->data->stretch;
ikConstraint->mix = ikConstraint->data->mix;
}

View File

@ -392,8 +392,9 @@ static spAnimation* _spSkeletonBinary_readAnimation (spSkeletonBinary* self, con
float time = readFloat(input);
float mix = readFloat(input);
signed char bendDirection = readSByte(input);
int compress = readBoolean(input);
int stretch = readBoolean(input);
spIkConstraintTimeline_setFrame(timeline, frameIndex, time, mix, bendDirection, stretch);
spIkConstraintTimeline_setFrame(timeline, frameIndex, time, mix, bendDirection, compress, stretch);
if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
}
spTimelineArray_add(timelines, (spTimeline*)timeline);
@ -958,7 +959,9 @@ spSkeletonData* spSkeletonBinary_readSkeletonData (spSkeletonBinary* self, const
data->target = skeletonData->bones[readVarint(input, 1)];
data->mix = readFloat(input);
data->bendDirection = readSByte(input);
data->compress = readBoolean(input);
data->stretch = readBoolean(input);
data->uniform = readBoolean(input);
skeletonData->ikConstraints[i] = data;
}

View File

@ -299,7 +299,7 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
}
for (valueMap = constraintMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
spIkConstraintTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), Json_getFloat(valueMap, "mix", 1),
Json_getInt(valueMap, "bendPositive", 1) ? 1 : -1, Json_getInt(valueMap, "stretch", 0) ? 1 : 0);
Json_getInt(valueMap, "bendPositive", 1) ? 1 : -1, Json_getInt(valueMap, "compress", 0) ? 1 : 0, Json_getInt(valueMap, "stretch", 0) ? 1 : 0);
readCurve(valueMap, SUPER(timeline), frameIndex);
}
animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
@ -737,7 +737,9 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
}
data->bendDirection = Json_getInt(constraintMap, "bendPositive", 1) ? 1 : -1;
data->compress = Json_getInt(constraintMap, "compress", 0) ? 1 : 0;
data->stretch = Json_getInt(constraintMap, "stretch", 0) ? 1 : 0;
data->uniform = Json_getInt(constraintMap, "uniform", 0) ? 1 : 0;
data->mix = Json_getFloat(constraintMap, "mix", 1);
skeletonData->ikConstraints[i] = data;

View File

@ -52,7 +52,7 @@ RTTI_DECL
public:
/// Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified
/// in the world coordinate system.
static void apply(Bone &bone, float targetX, float targetY, bool stretch, float alpha);
static void apply(Bone &bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha);
/// Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as
/// possible. The target is specified in the world coordinate system.
@ -80,6 +80,10 @@ public:
void setBendDirection(int inValue);
bool getCompress();
void setCompress(bool inValue);
bool getStretch();
void setStretch(bool inValue);
@ -92,6 +96,7 @@ private:
IkConstraintData &_data;
Vector<Bone *> _bones;
int _bendDirection;
bool _compress;
bool _stretch;
float _mix;
Bone *_target;

View File

@ -65,8 +65,14 @@ namespace spine {
int getBendDirection();
void setBendDirection(int inValue);
bool getCompress();
void setCompress(bool inValue);
bool getStretch();
void setStretch(bool inValue);
bool getUniform();
void setUniform(bool inValue);
float getMix();
void setMix(float inValue);
@ -77,7 +83,9 @@ namespace spine {
Vector<BoneData*> _bones;
BoneData* _target;
int _bendDirection;
bool _compress;
bool _stretch;
bool _uniform;
float _mix;
};
}

View File

@ -50,11 +50,11 @@ namespace spine {
virtual int getPropertyId();
/// Sets the time, mix and bend direction of the specified keyframe.
void setFrame (int frameIndex, float time, float mix, int bendDirection, bool stretch);
void setFrame (int frameIndex, float time, float mix, int bendDirection, bool compress, bool stretch);
private:
static const int PREV_TIME, PREV_MIX, PREV_BEND_DIRECTION, PREV_STRETCH;
static const int MIX, BEND_DIRECTION, STRETCH;
static const int PREV_TIME, PREV_MIX, PREV_BEND_DIRECTION, PREV_COMPRESS, PREV_STRETCH;
static const int MIX, BEND_DIRECTION, COMPRESS, STRETCH;
Vector<float> _frames;
int _ikConstraintIndex;

View File

@ -716,10 +716,10 @@ void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleto
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
float r1 = blend == MixBlend_Setup ? bone->_data._rotation : bone->_rotation;
float total, diff = r2 - r1;
diff -= (16384 - (int) (16384.499999999996 - diff / 360)) * 360;
if (diff == 0) {
total = timelinesRotation[i];
} else {
diff -= (16384 - (int) (16384.499999999996 - diff / 360)) * 360;
float lastTotal, lastDiff;
if (firstFrame) {
lastTotal = 0;

View File

@ -44,7 +44,7 @@ using namespace spine;
RTTI_IMPL(IkConstraint, Constraint)
void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool stretch, float alpha) {
void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha) {
Bone *p = bone.getParent();
float id, x, y, tx, ty, rotationIK;
if (!bone._appliedValid) bone.updateAppliedTransform();
@ -57,12 +57,17 @@ void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool stretch,
if (rotationIK > 180) rotationIK -= 360;
else if (rotationIK < -180) rotationIK += 360;
float sx = bone._ascaleX;
if (stretch) {
float sy = bone._ascaleY;
if (compress || stretch) {
float b = bone._data.getLength() * sx, dd = MathUtil::sqrt(tx * tx + ty * ty);
if (dd > b && b > 0.0001f) sx *= (dd / b - 1) * alpha + 1;
if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001f) {
float s = (dd / b - 1) * alpha + 1;
sx *= s;
if (uniform) sy *= s;
}
}
bone.updateWorldTransform(bone._ax, bone._ay, bone._arotation + rotationIK * alpha, sx,
bone._ascaleY, bone._ashearX, bone._ashearY);
sy, bone._ashearX, bone._ashearY);
}
void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY, int bendDir, bool stretch, float alpha) {
@ -206,6 +211,7 @@ void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY
IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Constraint(),
_data(data),
_bendDirection(data.getBendDirection()),
_compress(data.getCompress()),
_stretch(data.getStretch()),
_mix(data.getMix()),
_target(skeleton.findBone(
@ -226,7 +232,7 @@ void IkConstraint::update() {
switch (_bones.size()) {
case 1: {
Bone *bone0 = _bones[0];
apply(*bone0, _target->getWorldX(), _target->getWorldY(), _stretch, _mix);
apply(*bone0, _target->getWorldX(), _target->getWorldY(), _compress, _stretch, _data._uniform, _mix);
}
break;
case 2: {
@ -281,3 +287,11 @@ bool IkConstraint::getStretch() {
void IkConstraint::setStretch(bool inValue) {
_stretch = inValue;
}
bool IkConstraint::getCompress() {
return _compress;
}
void IkConstraint::setCompress(bool inValue) {
_compress = inValue;
}

View File

@ -43,7 +43,9 @@ IkConstraintData::IkConstraintData(const String &name) :
_order(0),
_target(NULL),
_bendDirection(1),
_compress(false),
_stretch(false),
_uniform(false),
_mix(1) {
}
@ -94,3 +96,20 @@ bool IkConstraintData::getStretch() {
void IkConstraintData::setStretch(bool inValue) {
_stretch = inValue;
}
bool IkConstraintData::getCompress() {
return _compress;
}
void IkConstraintData::setCompress(bool inValue) {
_compress = inValue;
}
bool IkConstraintData::getUniform() {
return _uniform;
}
void IkConstraintData::setUniform(bool inValue) {
_uniform = inValue;
}

View File

@ -48,14 +48,16 @@ using namespace spine;
RTTI_IMPL(IkConstraintTimeline, CurveTimeline)
const int IkConstraintTimeline::ENTRIES = 4;
const int IkConstraintTimeline::PREV_TIME = -4;
const int IkConstraintTimeline::PREV_MIX = -3;
const int IkConstraintTimeline::PREV_BEND_DIRECTION = -2;
const int IkConstraintTimeline::ENTRIES = 5;
const int IkConstraintTimeline::PREV_TIME = -5;
const int IkConstraintTimeline::PREV_MIX = -4;
const int IkConstraintTimeline::PREV_BEND_DIRECTION = -3;
const int IkConstraintTimeline::PREV_COMPRESS = -2;
const int IkConstraintTimeline::PREV_STRETCH = -1;
const int IkConstraintTimeline::MIX = 1;
const int IkConstraintTimeline::BEND_DIRECTION = 2;
const int IkConstraintTimeline::STRETCH = 3;
const int IkConstraintTimeline::COMPRESS = 3;
const int IkConstraintTimeline::STRETCH = 4;
IkConstraintTimeline::IkConstraintTimeline(int frameCount) : CurveTimeline(frameCount), _ikConstraintIndex(0) {
_frames.setSize(frameCount * ENTRIES, 0);
@ -73,11 +75,13 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
case MixBlend_Setup:
constraint._mix = constraint._data._mix;
constraint._bendDirection = constraint._data._bendDirection;
constraint._compress = constraint._data._compress;
constraint._stretch = constraint._data._stretch;
return;
case MixBlend_First:
constraint._mix += (constraint._data._mix - constraint._mix) * alpha;
constraint._bendDirection = constraint._data._bendDirection;
constraint._compress = constraint._data._compress;
constraint._stretch = constraint._data._stretch;
return;
default:
@ -92,15 +96,18 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
constraint._data._mix + (_frames[_frames.size() + PREV_MIX] - constraint._data._mix) * alpha;
if (direction == MixDirection_Out) {
constraint._bendDirection = constraint._data._bendDirection;
constraint._compress = constraint._data._compress;
constraint._stretch = constraint._data._stretch;
} else {
constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION];
constraint._compress = _frames[_frames.size() + PREV_COMPRESS] != 0;
constraint._stretch = _frames[_frames.size() + PREV_STRETCH] != 0;
}
} else {
constraint._mix += (_frames[_frames.size() + PREV_MIX] - constraint._mix) * alpha;
if (direction == MixDirection_In) {
constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION];
constraint._compress = _frames[_frames.size() + PREV_COMPRESS] != 0;
constraint._stretch = _frames[_frames.size() + PREV_STRETCH] != 0;
}
}
@ -119,15 +126,18 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
constraint._data._mix + (mix + (_frames[frame + MIX] - mix) * percent - constraint._data._mix) * alpha;
if (direction == MixDirection_Out) {
constraint._bendDirection = constraint._data._bendDirection;
constraint._compress = constraint._data._compress;
constraint._stretch = constraint._data._stretch;
} else {
constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION];
constraint._stretch = _frames[_frames.size() + PREV_STRETCH] != 0;
constraint._compress = _frames[frame + PREV_COMPRESS] != 0;
constraint._stretch = _frames[frame + PREV_STRETCH] != 0;
}
} else {
constraint._mix += (mix + (_frames[frame + MIX] - mix) * percent - constraint._mix) * alpha;
if (direction == MixDirection_In) {
constraint._bendDirection = (int) _frames[frame + PREV_BEND_DIRECTION];
constraint._compress = _frames[frame + PREV_COMPRESS] != 0;
constraint._stretch = _frames[frame + PREV_STRETCH] != 0;
}
}
@ -137,10 +147,11 @@ int IkConstraintTimeline::getPropertyId() {
return ((int) TimelineType_IkConstraint << 24) + _ikConstraintIndex;
}
void IkConstraintTimeline::setFrame(int frameIndex, float time, float mix, int bendDirection, bool stretch) {
void IkConstraintTimeline::setFrame(int frameIndex, float time, float mix, int bendDirection, bool compress, bool stretch) {
frameIndex *= ENTRIES;
_frames[frameIndex] = time;
_frames[frameIndex + MIX] = mix;
_frames[frameIndex + BEND_DIRECTION] = (float)bendDirection;
_frames[frameIndex + COMPRESS] = compress ? 1 : 0;
_frames[frameIndex + STRETCH] = stretch ? 1 : 0;
}

View File

@ -230,6 +230,7 @@ void Skeleton::setBonesToSetupPose() {
IkConstraint &constraint = *constraintP;
constraint._bendDirection = constraint._data._bendDirection;
constraint._compress = constraint._data._compress;
constraint._stretch = constraint._data._stretch;
constraint._mix = constraint._data._mix;
}

View File

@ -203,7 +203,9 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
data->_target = skeletonData->_bones[readVarint(input, true)];
data->_mix = readFloat(input);
data->_bendDirection = readSByte(input);
data->_compress = readBoolean(input);
data->_stretch = readBoolean(input);
data->_uniform = readBoolean(input);
skeletonData->_ikConstraints[i] = data;
}
@ -781,8 +783,9 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
float time = readFloat(input);
float mix = readFloat(input);
signed char bendDirection = readSByte(input);
bool compress = readBoolean(input);
bool stretch = readBoolean(input);
timeline->setFrame(frameIndex, time, mix, bendDirection, stretch);
timeline->setFrame(frameIndex, time, mix, bendDirection, compress, stretch);
if (frameIndex < frameCount - 1) {
readCurve(input, frameIndex, timeline);
}

View File

@ -285,9 +285,11 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
return NULL;
}
data->_bendDirection = Json::getInt(constraintMap, "bendPositive", 1) ? 1 : -1;
data->_stretch = Json::getInt(constraintMap, "stretch", 0) ? true: false;
data->_mix = Json::getFloat(constraintMap, "mix", 1);
data->_bendDirection = Json::getInt(constraintMap, "bendPositive", 1) ? 1 : -1;
data->_compress = Json::getInt(constraintMap, "compress", 0) ? true: false;
data->_stretch = Json::getInt(constraintMap, "stretch", 0) ? true: false;
data->_uniform = Json::getInt(constraintMap, "uniform", 0) ? true: false;
skeletonData->_ikConstraints[i] = data;
}
@ -932,7 +934,7 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
}
for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "mix", 1),
Json::getInt(valueMap, "bendPositive", 1) ? 1 : -1, Json::getInt(valueMap, "stretch", 0) ? true : false);
Json::getInt(valueMap, "bendPositive", 1) ? 1 : -1, Json::getInt(valueMap, "compress", 0) ? true : false, Json::getInt(valueMap, "stretch", 0) ? true : false);
readCurve(valueMap, timeline, frameIndex);
}
timelines.add(timeline);

View File

@ -1210,15 +1210,15 @@ namespace Spine {
}
public class IkConstraintTimeline : CurveTimeline {
public const int ENTRIES = 4;
private const int PREV_TIME = -4, PREV_MIX = -3, PREV_BEND_DIRECTION = -2, PREV_STRETCH = -1;
private const int MIX = 1, BEND_DIRECTION = 2, STRETCH = 3;
public const int ENTRIES = 5;
private const int PREV_TIME = -5, PREV_MIX = -4, PREV_BEND_DIRECTION = -3, PREV_COMPRESS = -2, PREV_STRETCH = -1;
private const int MIX = 1, BEND_DIRECTION = 2, COMPRESS = 3, STRETCH = 4;
internal int ikConstraintIndex;
internal float[] frames;
public int IkConstraintIndex { get { return ikConstraintIndex; } set { ikConstraintIndex = value; } }
public float[] Frames { get { return frames; } set { frames = value; } } // time, mix, bendDirection, ...
public float[] Frames { get { return frames; } set { frames = value; } } // time, mix, bendDirection, compress, stretch ...
override public int PropertyId {
get { return ((int)TimelineType.IkConstraint << 24) + ikConstraintIndex; }
@ -1229,12 +1229,13 @@ namespace Spine {
frames = new float[frameCount * ENTRIES];
}
/// <summary>Sets the time, mix and bend direction of the specified keyframe.</summary>
public void SetFrame (int frameIndex, float time, float mix, int bendDirection, bool stretch) {
/// <summary>Sets the time, mix, bend direction, compress and stretch of the specified keyframe.</summary>
public void SetFrame (int frameIndex, float time, float mix, int bendDirection, bool compress, bool stretch) {
frameIndex *= ENTRIES;
frames[frameIndex] = time;
frames[frameIndex + MIX] = mix;
frames[frameIndex + BEND_DIRECTION] = bendDirection;
frames[frameIndex + COMPRESS] = compress ? 1 : 0;
frames[frameIndex + STRETCH] = stretch ? 1 : 0;
}
@ -1246,11 +1247,13 @@ namespace Spine {
case MixBlend.Setup:
constraint.mix = constraint.data.mix;
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
return;
case MixBlend.First:
constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
return;
}
@ -1262,15 +1265,18 @@ namespace Spine {
constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha;
if (direction == MixDirection.Out) {
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
} else {
constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
constraint.compress = frames[frames.Length + PREV_COMPRESS] != 0;
constraint.stretch = frames[frames.Length + PREV_STRETCH] != 0;
}
} else {
constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
if (direction == MixDirection.In) {
constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
constraint.compress = frames[frames.Length + PREV_COMPRESS] != 0;
constraint.stretch = frames[frames.Length + PREV_STRETCH] != 0;
}
}
@ -1285,10 +1291,22 @@ namespace Spine {
if (blend == MixBlend.Setup) {
constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
constraint.bendDirection = direction == MixDirection.Out ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION];
if (direction == MixDirection.Out) {
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
} else {
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
constraint.compress = frames[frame + PREV_COMPRESS] != 0;
constraint.stretch = frames[frame + PREV_STRETCH] != 0;
}
} else {
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
if (direction == MixDirection.In) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
if (direction == MixDirection.In) {
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
constraint.compress = frames[frame + PREV_COMPRESS] != 0;
constraint.stretch = frames[frame + PREV_STRETCH] != 0;
}
}
}
}

View File

@ -36,7 +36,7 @@ namespace Spine {
internal ExposedList<Bone> bones = new ExposedList<Bone>();
internal Bone target;
internal int bendDirection;
internal bool stretch;
internal bool compress, stretch;
internal float mix;
public IkConstraintData Data { get { return data; } }
@ -59,9 +59,17 @@ namespace Spine {
set { bendDirection = value; }
}
/// <summary>
/// When true and only a single bone is being constrained,
/// if the target is too close, the bone is scaled to reach it.</summary>
public bool Compress {
get { return compress; }
set { compress = value; }
}
/// <summary>
/// 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 nonuniform scale, stretching is not applied.</summary>
/// If the parent bone has nonuniform scale, stretching is not applied.</summary>
public bool Stretch {
get { return stretch; }
set { stretch = value; }
@ -79,6 +87,7 @@ namespace Spine {
this.data = data;
mix = data.mix;
bendDirection = data.bendDirection;
compress = data.compress;
stretch = data.stretch;
bones = new ExposedList<Bone>(data.bones.Count);
@ -97,7 +106,7 @@ namespace Spine {
ExposedList<Bone> bones = this.bones;
switch (bones.Count) {
case 1:
Apply(bones.Items[0], target.worldX, target.worldY, stretch, mix);
Apply(bones.Items[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
break;
case 2:
Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, stretch, mix);
@ -109,9 +118,8 @@ namespace Spine {
return data.name;
}
/// <summary>Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified
/// in the world coordinate system.</summary>
static public void Apply (Bone bone, float targetX, float targetY, bool stretch, float alpha) {
/// <summary>Applies 1 bone IK. The target is specified in the world coordinate system.</summary>
static public void Apply (Bone bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha) {
if (!bone.appliedValid) bone.UpdateAppliedTransform();
Bone p = bone.parent;
float id = 1 / (p.a * p.d - p.b * p.c);
@ -121,14 +129,18 @@ namespace Spine {
if (bone.ascaleX < 0) rotationIK += 180;
if (rotationIK > 180)
rotationIK -= 360;
else if (rotationIK < -180)
else if (rotationIK < -180) //
rotationIK += 360;
float sx = bone.ascaleX;
if (stretch) {
float sx = bone.ascaleX, sy = bone.ascaleY;
if (compress || 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;
if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001f) {
float s = (dd / b - 1) * alpha + 1;
sx *= s;
if (uniform) sy *= s;
}
}
bone.UpdateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, bone.ascaleY, bone.ashearX,
bone.UpdateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX,
bone.ashearY);
}

View File

@ -39,7 +39,7 @@ namespace Spine {
internal List<BoneData> bones = new List<BoneData>();
internal BoneData target;
internal int bendDirection = 1;
internal bool stretch;
internal bool compress, stretch, uniform;
internal float mix = 1;
/// <summary>The IK constraint's name, which is unique within the skeleton.</summary>
@ -63,12 +63,27 @@ namespace Spine {
set { target = value; }
}
/// <summary>
/// A percentage (0-1) that controls the mix between the constraint and unconstrained rotations.</summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>
/// When true, and only a single bone is being constrained,
/// if the target is too close, the bone is scaled to reach it. </summary>
public bool Compress {
get { return compress; }
set { compress = value; }
}
/// <summary>
/// When true, if the target is out of range, the parent bone is scaled on the X axis to reach it.
/// If the bone has local nonuniform scale, stretching is not applied.</summary>
@ -77,7 +92,13 @@ namespace Spine {
set { stretch = value; }
}
public float Mix { get { return mix; } set { mix = value; } }
/// <summary>
/// When true, only a single bone is being constrained and Compress or Stretch is used,
/// the bone is scaled both on the X and Y axes.</summary>
public bool Uniform {
get { return uniform; }
set { uniform = value; }
}
public IkConstraintData (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");

View File

@ -308,9 +308,10 @@ namespace Spine {
var ikConstraintsItems = this.ikConstraints.Items;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraint constraint = ikConstraintsItems[i];
constraint.bendDirection = constraint.data.bendDirection;
constraint.stretch = constraint.data.stretch;
constraint.mix = constraint.data.mix;
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
}
var transformConstraintsItems = this.transformConstraints.Items;

View File

@ -210,7 +210,9 @@ namespace Spine {
data.target = skeletonData.bones.Items[ReadVarint(input, true)];
data.mix = ReadFloat(input);
data.bendDirection = ReadSByte(input);
data.compress = ReadBoolean(input);
data.stretch = ReadBoolean(input);
data.uniform = ReadBoolean(input);
skeletonData.ikConstraints.Add(data);
}
@ -646,10 +648,11 @@ namespace Spine {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
int index = ReadVarint(input, true);
int frameCount = ReadVarint(input, true);
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
timeline.ikConstraintIndex = index;
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount) {
ikConstraintIndex = index
};
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input), ReadBoolean(input));
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input), ReadBoolean(input), ReadBoolean(input));
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);

View File

@ -180,10 +180,11 @@ namespace Spine {
string targetName = (string)constraintMap["target"];
data.target = skeletonData.FindBone(targetName);
if (data.target == null) throw new Exception("Target bone not found: " + targetName);
data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1;
data.stretch = GetBoolean(constraintMap, "stretch", false);
data.mix = GetFloat(constraintMap, "mix", 1);
data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1;
data.compress = GetBoolean(constraintMap, "compress", false);
data.stretch = GetBoolean(constraintMap, "stretch", false);
data.uniform = GetBoolean(constraintMap, "uniform", false);
skeletonData.ikConstraints.Add(data);
}
@ -595,11 +596,14 @@ namespace Spine {
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
float time = (float)valueMap["time"];
float mix = GetFloat(valueMap, "mix", 1);
bool bendPositive = GetBoolean(valueMap, "bendPositive", true);
bool stretch = GetBoolean(valueMap, "stretch", false);
timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1, stretch);
timeline.SetFrame(
frameIndex,
(float)valueMap["time"],
GetFloat(valueMap, "mix", 1),
GetBoolean(valueMap, "bendPositive", true) ? 1 : -1,
GetBoolean(valueMap, "compress", true),
GetBoolean(valueMap, "stretch", false)
);
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}

View File

@ -1169,19 +1169,21 @@ function Animation.DrawOrderTimeline.new (frameCount)
end
Animation.IkConstraintTimeline = {}
Animation.IkConstraintTimeline.ENTRIES = 4
Animation.IkConstraintTimeline.ENTRIES = 5
function Animation.IkConstraintTimeline.new (frameCount)
local ENTRIES = Animation.IkConstraintTimeline.ENTRIES
local PREV_TIME = -4
local PREV_MIX = -3
local PREV_BEND_DIRECTION = -2
local PREV_TIME = -5
local PREV_MIX = -4
local PREV_BEND_DIRECTION = -3
local PREV_COMPRESS = -2
local PREV_STRETCH = -1
local MIX = 1
local BEND_DIRECTION = 2
local COMPRESS = 3
local STRETCH = 1
local self = Animation.CurveTimeline.new(frameCount)
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) -- time, mix, bendDirection, ...
self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) -- time, mix, bendDirection, compress, stretch, ...
self.ikConstraintIndex = -1
self.type = TimelineType.ikConstraint
@ -1189,11 +1191,16 @@ function Animation.IkConstraintTimeline.new (frameCount)
return TimelineType.ikConstraint * SHL_24 + self.ikConstraintIndex
end
function self:setFrame (frameIndex, time, mix, bendDirection, stretch)
function self:setFrame (frameIndex, time, mix, bendDirection, compress, stretch)
frameIndex = frameIndex * ENTRIES
self.frames[frameIndex] = time
self.frames[frameIndex + MIX] = mix
self.frames[frameIndex + BEND_DIRECTION] = bendDirection
if (compress) then
self.frames[frameIndex + COMPRESS] = 1
else
self.frames[frameIndex + COMPRESS] = 0
end
if (stretch) then
self.frames[frameIndex + STRETCH] = 1
else
@ -1209,10 +1216,12 @@ function Animation.IkConstraintTimeline.new (frameCount)
if blend == MixBlend.setup then
constraint.mix = constraint.data.mix
constraint.bendDirection = constraint.data.bendDirection
constraint.compress = constraint.data.compress
constraint.stretch = constraint.data.stretch
elseif blend == MixBlend.first then
constraint.mix = constraint.mix + (constraint.data.mix - constraint.mix) * alpha
constraint.bendDirection = constraint.data.bendDirection
constraint.compress = constraint.data.compress
constraint.stretch = constraint.data.stretch
end
return
@ -1223,15 +1232,18 @@ function Animation.IkConstraintTimeline.new (frameCount)
constraint.mix = constraint.data.mix + (frames[zlen(frames) + PREV_MIX] - constraint.data.mix) * alpha
if direction == MixDirection.out then
constraint.bendDirection = constraint.data.bendDirection
constraint.compress = constraint.data.compress
constraint.stretch = constraint.data.stretch
else
constraint.bendDirection = math_floor(frames[zlen(frames) + PREV_BEND_DIRECTION]);
if (math_floor(frames[zlen(frames) + PREV_COMPRESS]) == 1) then constraint.compress = true else constraint.compress = false end
if (math_floor(frames[zlen(frames) + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end
end
else
constraint.mix = constraint.mix + (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
if direction == MixDirection._in then
constraint.bendDirection = math_floor(frames[zlen(frames) + PREV_BEND_DIRECTION])
if (math_floor(frames[zlen(frames) + PREV_COMPRES]) == 1) then constraint.compress = true else constraint.compress = false end
if (math_floor(frames[zlen(frames) + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end
end
end
@ -1249,15 +1261,18 @@ function Animation.IkConstraintTimeline.new (frameCount)
constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha
if direction == MixDirection.out then
constraint.bendDirection = constraint.data.bendDirection
constraint.compress = constraint.data.compress
constraint.stretch = constraint.data.stretch
else
constraint.bendDirection = math_floor(frames[frame + PREV_BEND_DIRECTION])
if (math_floor(frames[frame + PREV_COMPRESS]) == 1) then constraint.compress = true else constraint.compress = false end
if (math_floor(frames[frame + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end
end
else
constraint.mix = constraint.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
if direction == MixDirection._in then
constraint.bendDirection = math_floor(frames[frame + PREV_BEND_DIRECTION])
if (math_floor(frames[frame + PREV_COMPRESS]) == 1) then constraint.compress = true else constraint.compress = false end
if (math_floor(frames[frame + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end
end
end

View File

@ -543,10 +543,10 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, bl
if blend == MixBlend.setup then r1 = bone.data.rotation end
local total = 0
local diff = r2 - r1
diff = diff - (16384 - math_floor(16384.499999999996 - diff / 360)) * 360
if diff == 0 then
total = timelinesRotation[i]
else
diff = diff - (16384 - math_floor(16384.499999999996 - diff / 360)) * 360
local lastTotal = 0
local lastDiff = 0
if firstFrame then

View File

@ -52,6 +52,7 @@ function IkConstraint.new (data, skeleton)
bones = {},
target = nil,
mix = data.mix,
compress = data.compress,
stretch = data.stretch,
bendDirection = data.bendDirection,
}
@ -75,13 +76,13 @@ function IkConstraint:update ()
local bones = self.bones
local boneCount = #bones
if boneCount == 1 then
self:apply1(bones[1], target.worldX, target.worldY, self.stretch, self.mix)
self:apply1(bones[1], target.worldX, target.worldY, self.compress, self.stretch, self.data.uniform, self.mix)
elseif boneCount == 2 then
self:apply2(bones[1], bones[2], target.worldX, target.worldY, self.bendDirection, self.stretch, self.mix)
end
end
function IkConstraint:apply1 (bone, targetX, targetY, stretch, alpha)
function IkConstraint:apply1 (bone, targetX, targetY, compress, stretch, uniform, alpha)
if not bone.appliedValid then bone:updateAppliedTransform() end
local p = bone.parent
local id = 1 / (p.a * p.d - p.b * p.c)
@ -97,11 +98,17 @@ function IkConstraint:apply1 (bone, targetX, targetY, stretch, alpha)
rotationIK = rotationIK + 360
end
local sx = bone.ascaleX
if stretch then
local sy = bone.ascaleY
if compress or stretch then
local b = bone.data.length * sx
local dd = math_sqrt(tx * tx + ty * ty)
if dd > bone.data.length * sx then sx = sx * ((dd / (bone.data.length * sx) - 1) * alpha + 1) end
if (compress and dd < b) or (stretch and dd > b) and b > 0.0001 then
local s = (dd / b - 1) * alpha + 1
sx = sx * s
if uniform then sy = sy * s end
end
end
bone:updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, bone.ascaleY, bone.ashearX, bone.ashearY)
bone:updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY)
end
function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, alpha)

View File

@ -38,7 +38,9 @@ function IkConstraintData.new (name)
bones = {},
target = nil,
bendDirection = 1,
compress = false,
stretch = false,
uniform = false,
mix = 1
}

View File

@ -338,9 +338,10 @@ function Skeleton:setBonesToSetupPose ()
end
for _,ikConstraint in ipairs(self.ikConstraints) do
ikConstraint.bendDirection = ikConstraint.data.bendDirection
ikConstraint.stretch = ikConstraint.data.stretch
ikConstraint.mix = ikConstraint.data.mix
ikConstraint.bendDirection = ikConstraint.data.bendDirection
ikConstraint.compress = ikConstraint.data.compress
ikConstraint.stretch = ikConstraint.data.stretch
end
local transformConstraints = self.transformConstraints

View File

@ -164,9 +164,11 @@ function SkeletonJson.new (attachmentLoader)
data.target = skeletonData:findBone(targetName)
if not data.target then error("Target bone not found: " .. targetName) end
if constraintMap["bendPositive"] == false then data.bendDirection = -1 else data.bendDirection = 1 end
if constraintMap["stretch"] == false then data.stretch = false else data.stretch = true end
data.mix = getValue(constraintMap, "mix", 1)
if constraintMap["bendPositive"] == nil or constraintMap["bendPositive"] == false then data.bendDirection = -1 else data.bendDirection = 1 end
if constraintMap["compress"] == nil or constraintMap["compress"] == false then data.compress = false else data.compress = true end
if constraintMap["stretch"] == nil or constraintMap["stretch"] == false then data.stretch = false else data.stretch = true end
if constraintMap["uniform"] == nil or constraintMap["uniform"] == false then data.uniform = false else data.uniform = true end
table_insert(skeletonData.ikConstraints, data)
end
@ -613,9 +615,11 @@ function SkeletonJson.new (attachmentLoader)
if valueMap["mix"] ~= nil then mix = valueMap["mix"] end
local bendPositive = 1
if valueMap["bendPositive"] == false then bendPositive = -1 end
local stretch = true
if valueMap["stretch"] == false then stretch = false end
timeline:setFrame(frameIndex, valueMap["time"], mix, bendPositive, stretch)
local stretch = false
if valueMap["stretch"] ~= nil then stretch = valueMap["stretch"] end
local compress = false
if valueMap["compress"] ~= nil then compress = valueMap["compress"] end
timeline:setFrame(frameIndex, valueMap["time"], mix, bendPositive, compress, stretch)
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1037,12 +1037,12 @@ module spine {
}
export class IkConstraintTimeline extends CurveTimeline {
static ENTRIES = 4;
static PREV_TIME = -4; static PREV_MIX = -3; static PREV_BEND_DIRECTION = -2; static PREV_STRETCH = -1;
static MIX = 1; static BEND_DIRECTION = 2; static STRETCH = 3;
static ENTRIES = 5;
static PREV_TIME = -5; static PREV_MIX = -4; static PREV_BEND_DIRECTION = -3; static PREV_COMPRESS = -2; static PREV_STRETCH = -1;
static MIX = 1; static BEND_DIRECTION = 2; static COMPRESS = 3; static STRETCH = 4;
ikConstraintIndex: number;
frames: ArrayLike<number>; // time, mix, bendDirection, ...
frames: ArrayLike<number>; // time, mix, bendDirection, compress, stretch, ...
constructor (frameCount: number) {
super(frameCount);
@ -1054,11 +1054,12 @@ module spine {
}
/** Sets the time, mix and bend direction of the specified keyframe. */
setFrame (frameIndex: number, time: number, mix: number, bendDirection: number, stretch: boolean) {
setFrame (frameIndex: number, time: number, mix: number, bendDirection: number, compress: boolean, stretch: boolean) {
frameIndex *= IkConstraintTimeline.ENTRIES;
this.frames[frameIndex] = time;
this.frames[frameIndex + IkConstraintTimeline.MIX] = mix;
this.frames[frameIndex + IkConstraintTimeline.BEND_DIRECTION] = bendDirection;
this.frames[frameIndex + IkConstraintTimeline.COMPRESS] = compress ? 1 : 0;
this.frames[frameIndex + IkConstraintTimeline.STRETCH] = stretch ? 1 : 0;
}
@ -1070,11 +1071,13 @@ module spine {
case MixBlend.setup:
constraint.mix = constraint.data.mix;
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
return;
case MixBlend.first:
constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
}
return;
@ -1085,15 +1088,18 @@ module spine {
constraint.mix = constraint.data.mix + (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.data.mix) * alpha;
if (direction == MixDirection.out) {
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
} else {
constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION]
constraint.compress = frames[frames.length + IkConstraintTimeline.PREV_COMPRESS] != 0;
constraint.stretch = frames[frames.length + IkConstraintTimeline.PREV_STRETCH] != 0;
}
} else {
constraint.mix += (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.mix) * alpha;
if (direction == MixDirection.in) {
constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION];
constraint.compress = frames[frames.length + IkConstraintTimeline.PREV_COMPRESS] != 0;
constraint.stretch = frames[frames.length + IkConstraintTimeline.PREV_STRETCH] != 0;
}
}
@ -1111,15 +1117,18 @@ module spine {
constraint.mix = constraint.data.mix + (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.data.mix) * alpha;
if (direction == MixDirection.out) {
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
} else {
constraint.bendDirection = frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION];
constraint.compress = frames[frame + IkConstraintTimeline.PREV_COMPRESS] != 0;
constraint.stretch = frames[frame + IkConstraintTimeline.PREV_STRETCH] != 0;
}
} else {
constraint.mix += (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.mix) * alpha;
if (direction == MixDirection.in) {
constraint.bendDirection = frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION];
constraint.compress = frames[frame + IkConstraintTimeline.PREV_COMPRESS] != 0;
constraint.stretch = frames[frame + IkConstraintTimeline.PREV_STRETCH] != 0;
}
}

View File

@ -304,10 +304,10 @@ module spine {
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
let r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation;
let total = 0, diff = r2 - r1;
diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360;
if (diff == 0) {
total = timelinesRotation[i];
} else {
diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360;
let lastTotal = 0, lastDiff = 0;
if (firstFrame) {
lastTotal = 0;

View File

@ -34,6 +34,7 @@ module spine {
bones: Array<Bone>;
target: Bone;
bendDirection = 0;
compress = false;
stretch = false;
mix = 1;
@ -43,6 +44,7 @@ module spine {
this.data = data;
this.mix = data.mix;
this.bendDirection = data.bendDirection;
this.compress = data.compress;
this.stretch = data.stretch;
this.bones = new Array<Bone>();
@ -64,7 +66,7 @@ module spine {
let bones = this.bones;
switch (bones.length) {
case 1:
this.apply1(bones[0], target.worldX, target.worldY, this.stretch, this.mix);
this.apply1(bones[0], target.worldX, target.worldY, this.compress, this.stretch, this.data.uniform, this.mix);
break;
case 2:
this.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.stretch, this.mix);
@ -74,7 +76,7 @@ module spine {
/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world
* coordinate system. */
apply1 (bone: Bone, targetX: number, targetY: number, stretch: boolean, alpha: number) {
apply1 (bone: Bone, targetX: number, targetY: number, compress: boolean, stretch: boolean, uniform: boolean, alpha: number) {
if (!bone.appliedValid) bone.updateAppliedTransform();
let p = bone.parent;
let id = 1 / (p.a * p.d - p.b * p.c);
@ -85,12 +87,16 @@ module spine {
if (rotationIK > 180)
rotationIK -= 360;
else if (rotationIK < -180) rotationIK += 360;
let sx = bone.ascaleX;
if (stretch) {
let sx = bone.ascaleX, sy = bone.ascaleY;
if (compress || stretch) {
let b = bone.data.length * sx, dd = Math.sqrt(tx * tx + ty * ty);
if (dd > b && b > 0.0001) sx *= (dd / b - 1) * alpha + 1;
if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001) {
let s = (dd / b - 1) * alpha + 1;
sx *= s;
if (uniform) sy *= s;
}
}
bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, bone.ascaleY, bone.ashearX,
bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX,
bone.ashearY);
}

View File

@ -35,7 +35,9 @@ module spine {
bones = new Array<BoneData>();
target: BoneData;
bendDirection = 1;
compress = false;
stretch = false;
uniform = false;
mix = 1;
constructor (name: string) {

View File

@ -286,9 +286,10 @@ module spine {
let ikConstraints = this.ikConstraints;
for (let i = 0, n = ikConstraints.length; i < n; i++) {
let constraint = ikConstraints[i];
constraint.bendDirection = constraint.data.bendDirection;
constraint.stretch = constraint.data.stretch;
constraint.mix = constraint.data.mix;
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
}
let transformConstraints = this.transformConstraints;

View File

@ -123,9 +123,11 @@ module spine {
data.target = skeletonData.findBone(targetName);
if (data.target == null) throw new Error("IK target bone not found: " + targetName);
data.bendDirection = this.getValue(constraintMap, "bendPositive", true) ? 1 : -1;
data.stretch = this.getValue(constraintMap, "stretch", false);
data.mix = this.getValue(constraintMap, "mix", 1);
data.bendDirection = this.getValue(constraintMap, "bendPositive", true) ? 1 : -1;
data.compress = this.getValue(constraintMap, "compress", false);
data.stretch = this.getValue(constraintMap, "stretch", false);
data.uniform = this.getValue(constraintMap, "uniform", false);
skeletonData.ikConstraints.push(data);
}
@ -521,7 +523,7 @@ module spine {
for (let i = 0; i < constraintMap.length; i++) {
let valueMap = constraintMap[i];
timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "mix", 1),
this.getValue(valueMap, "bendPositive", true) ? 1 : -1, this.getValue(valueMap, "stretch", false));
this.getValue(valueMap, "bendPositive", true) ? 1 : -1, this.getValue(valueMap, "compress", false), this.getValue(valueMap, "stretch", false));
this.readCurve(valueMap, timeline, frameIndex);
frameIndex++;
}

View File

@ -371,12 +371,16 @@ namespace Spine.Unity.Editor {
foreach (var c in skeleton.IkConstraints) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintIK));
FalseDropDown("Goal", c.Data.Target.Name, Icons.bone, true);
using (new EditorGUI.DisabledGroupScope(true)) {
EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Data.Uniform", tooltip: "Uniformly scales a bone when Ik stretches or compresses."), c.Data.Uniform);
}
EditorGUI.BeginChangeCheck();
c.Mix = EditorGUILayout.Slider("Mix", c.Mix, MixMin, MixMax);
c.BendDirection = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bend Clockwise", tooltip: "IkConstraint.BendDirection == 1 if clockwise; -1 if counterclockwise."), c.BendDirection > 0) ? 1 : -1;
c.Compress = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Compress", tooltip: "Compress single bone IK when the target too close. Not applied when parent bone has nonuniform scale."), c.Compress);
c.Stretch = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Stretch", tooltip: "Stretch the parent bone when the target is out of range. Not applied when parent bone has nonuniform scale."), c.Stretch);
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
EditorGUILayout.Space();
}