From 7a19650b5b088b81ce75fc4dfd57a8be5d244548 Mon Sep 17 00:00:00 2001 From: badlogic Date: Wed, 19 Jun 2019 16:13:09 +0200 Subject: [PATCH] [c] Added soft IK support. See #1383. --- spine-c/spine-c/include/spine/Animation.h | 4 +- spine-c/spine-c/include/spine/IkConstraint.h | 4 +- .../spine-c/include/spine/IkConstraintData.h | 4 +- spine-c/spine-c/src/spine/Animation.c | 18 +++++-- spine-c/spine-c/src/spine/IkConstraint.c | 47 ++++++++++++++----- spine-c/spine-c/src/spine/Skeleton.c | 1 + spine-c/spine-c/src/spine/SkeletonBinary.c | 4 +- spine-c/spine-c/src/spine/SkeletonJson.c | 3 +- 8 files changed, 63 insertions(+), 22 deletions(-) diff --git a/spine-c/spine-c/include/spine/Animation.h b/spine-c/spine-c/include/spine/Animation.h index df2bc2625..850b96dcd 100644 --- a/spine-c/spine-c/include/spine/Animation.h +++ b/spine-c/spine-c/include/spine/Animation.h @@ -431,7 +431,7 @@ typedef spDeformTimeline DeformTimeline; /**/ -static const int IKCONSTRAINT_ENTRIES = 5; +static const int IKCONSTRAINT_ENTRIES = 6; typedef struct spIkConstraintTimeline { spCurveTimeline super; @@ -451,7 +451,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*/ compress, int /**boolean**/ stretch); +SP_API void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, float softness, int bendDirection, int /*boolean*/ compress, int /**boolean**/ stretch); #ifdef SPINE_SHORT_NAMES typedef spIkConstraintTimeline IkConstraintTimeline; diff --git a/spine-c/spine-c/include/spine/IkConstraint.h b/spine-c/spine-c/include/spine/IkConstraint.h index f56f8222a..0b8dcbb46 100644 --- a/spine-c/spine-c/include/spine/IkConstraint.h +++ b/spine-c/spine-c/include/spine/IkConstraint.h @@ -51,6 +51,7 @@ typedef struct spIkConstraint { int /*boolean*/ compress; int /*boolean*/ stretch; float mix; + float softness; int /*boolean*/ active; @@ -63,6 +64,7 @@ typedef struct spIkConstraint { bendDirection(0), stretch(0), mix(0), + softness(0), active(0) { } #endif @@ -74,7 +76,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*/ 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); +SP_API void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDirection, int /*boolean*/ stretch, float softness, float alpha); #ifdef SPINE_SHORT_NAMES typedef spIkConstraint IkConstraint; diff --git a/spine-c/spine-c/include/spine/IkConstraintData.h b/spine-c/spine-c/include/spine/IkConstraintData.h index 09e35a989..6ccd92e49 100644 --- a/spine-c/spine-c/include/spine/IkConstraintData.h +++ b/spine-c/spine-c/include/spine/IkConstraintData.h @@ -50,6 +50,7 @@ typedef struct spIkConstraintData { int /*boolean*/ stretch; int /*boolean*/ uniform; float mix; + float softness; #ifdef __cplusplus spIkConstraintData() : @@ -63,7 +64,8 @@ typedef struct spIkConstraintData { compress(0), stretch(0), uniform(0), - mix(0) { + mix(0), + softness(0) { } #endif } spIkConstraintData; diff --git a/spine-c/spine-c/src/spine/Animation.c b/spine-c/spine-c/src/spine/Animation.c index aec586d97..e7a38286c 100644 --- a/spine-c/spine-c/src/spine/Animation.c +++ b/spine-c/spine-c/src/spine/Animation.c @@ -1290,13 +1290,13 @@ void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIndex, fl /**/ -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; +static const int IKCONSTRAINT_PREV_TIME = -6, IKCONSTRAINT_PREV_MIX = -5, IKCONSTRAINT_PREV_SOFTNESS = -4, IKCONSTRAINT_PREV_BEND_DIRECTION = -3, IKCONSTRAINT_PREV_COMPRESS = -2, IKCONSTRAINT_PREV_STRETCH = -1; +static const int IKCONSTRAINT_MIX = 1, IKCONSTRAINT_SOFTNESS = 2, IKCONSTRAINT_BEND_DIRECTION = 3, IKCONSTRAINT_COMPRESS = 4, IKCONSTRAINT_STRETCH = 5; void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents, int* eventsCount, float alpha, spMixBlend blend, spMixDirection direction) { int frame; - float frameTime, percent, mix; + float frameTime, percent, mix, softness; float *frames; int framesCount; spIkConstraint* constraint; @@ -1309,12 +1309,14 @@ void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skel switch (blend) { case SP_MIX_BLEND_SETUP: constraint->mix = constraint->data->mix; + constraint->softness = constraint->data->softness; 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->softness += (constraint->data->softness - constraint->softness) * alpha; constraint->bendDirection = constraint->data->bendDirection; constraint->compress = constraint->data->compress; constraint->stretch = constraint->data->stretch; @@ -1330,6 +1332,8 @@ void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skel if (time >= frames[framesCount - IKCONSTRAINT_ENTRIES]) { /* Time is after last frame. */ if (blend == SP_MIX_BLEND_SETUP) { constraint->mix = constraint->data->mix + (frames[framesCount + IKCONSTRAINT_PREV_MIX] - constraint->data->mix) * alpha; + constraint->softness = constraint->data->softness + + (frames[framesCount + IKCONSTRAINT_PREV_SOFTNESS] - constraint->data->softness) * alpha; if (direction == SP_MIX_DIRECTION_OUT) { constraint->bendDirection = constraint->data->bendDirection; constraint->compress = constraint->data->compress; @@ -1341,6 +1345,7 @@ void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skel } } else { constraint->mix += (frames[framesCount + IKCONSTRAINT_PREV_MIX] - constraint->mix) * alpha; + constraint->softness += (frames[framesCount + IKCONSTRAINT_PREV_SOFTNESS] - constraint->softness) * 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; @@ -1353,11 +1358,14 @@ void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skel /* Interpolate between the previous frame and the current frame. */ frame = binarySearch(self->frames, self->framesCount, time, IKCONSTRAINT_ENTRIES); mix = self->frames[frame + IKCONSTRAINT_PREV_MIX]; + softness = frames[frame + IKCONSTRAINT_PREV_SOFTNESS]; frameTime = self->frames[frame]; percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / IKCONSTRAINT_ENTRIES - 1, 1 - (time - frameTime) / (self->frames[frame + IKCONSTRAINT_PREV_TIME] - frameTime)); if (blend == SP_MIX_BLEND_SETUP) { constraint->mix = constraint->data->mix + (mix + (frames[frame + IKCONSTRAINT_MIX] - mix) * percent - constraint->data->mix) * alpha; + constraint->softness = constraint->data->softness + + (softness + (frames[frame + IKCONSTRAINT_SOFTNESS] - softness) * percent - constraint->data->softness) * alpha; if (direction == SP_MIX_DIRECTION_OUT) { constraint->bendDirection = constraint->data->bendDirection; constraint->compress = constraint->data->compress; @@ -1369,6 +1377,7 @@ void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skel } } else { constraint->mix += (mix + (frames[frame + IKCONSTRAINT_MIX] - mix) * percent - constraint->mix) * alpha; + constraint->softness += (softness + (frames[frame + IKCONSTRAINT_SOFTNESS] - softness) * percent - constraint->softness) * 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; @@ -1389,10 +1398,11 @@ 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*/ compress, int /*boolean*/ stretch) { +void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, float softness, 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_SOFTNESS] = softness; self->frames[frameIndex + IKCONSTRAINT_BEND_DIRECTION] = (float)bendDirection; self->frames[frameIndex + IKCONSTRAINT_COMPRESS] = compress ? 1 : 0; self->frames[frameIndex + IKCONSTRAINT_STRETCH] = stretch ? 1 : 0; diff --git a/spine-c/spine-c/src/spine/IkConstraint.c b/spine-c/spine-c/src/spine/IkConstraint.c index d12f6aefc..fe281924c 100644 --- a/spine-c/spine-c/src/spine/IkConstraint.c +++ b/spine-c/spine-c/src/spine/IkConstraint.c @@ -41,6 +41,7 @@ spIkConstraint *spIkConstraint_create(spIkConstraintData *data, const spSkeleton self->compress = data->compress; self->stretch = data->stretch; self->mix = data->mix; + self->softness = data->softness; self->bonesCount = self->data->bonesCount; self->bones = MALLOC(spBone*, self->bonesCount); @@ -62,7 +63,7 @@ void spIkConstraint_apply(spIkConstraint *self) { 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); + spIkConstraint_apply2(self->bones[0], self->bones[1], self->target->worldX, self->target->worldY, self->bendDirection, self->stretch, self->softness, self->mix); break; } } @@ -92,12 +93,13 @@ void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, int /*bo sy, bone->ashearX, bone->ashearY); } -void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDir, int /*boolean*/ stretch, float alpha) { +void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDir, int /*boolean*/ stretch, float softness, float alpha) { + float a, b, c, d; float px, py, psx, sx, psy; float cx, cy, csx, cwx, cwy; int o1, o2, s2, u; spBone* pp = parent->parent; - float tx, ty, dd, dx, dy, l1, l2, a1, a2, r; + float tx, ty, dd, dx, dy, l1, l2, a1, a2, r, td, sd, p; float id, x, y; if (alpha == 0) { spBone_updateWorldTransform(child); @@ -135,18 +137,39 @@ void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float cwx = parent->a * cx + parent->b * cy + parent->worldX; cwy = parent->c * cx + parent->d * cy + parent->worldY; } - id = 1 / (pp->a * pp->d - pp->b * pp->c); - x = targetX - pp->worldX; - y = targetY - pp->worldY; - tx = (x * pp->d - y * pp->b) * id - px; - ty = (y * pp->a - x * pp->c) * id - py; - dd = tx * tx + ty * ty; + a = pp->a; + b = pp->b; + c = pp->c; + d = pp->d; + id = 1 / (a * d - b * c); x = cwx - pp->worldX; y = cwy - pp->worldY; - dx = (x * pp->d - y * pp->b) * id - px; - dy = (y * pp->a - x * pp->c) * id - py; + dx = (x * d - y * b) * id - px; + dy = (y * a - x * c) * id - py; l1 = SQRT(dx * dx + dy * dy); l2 = child->data->length * csx; + if (l1 < 0.0001) { + spIkConstraint_apply1(parent, targetX, targetY, 0, stretch, 0, alpha); + spBone_updateWorldTransformWith(child, cx, cy, 0, child->ascaleX, child->ascaleY, child->ashearX, child->ashearY); + return; + } + x = targetX - pp->worldX; + y = targetY - pp->worldY; + tx = (x * d - y * b) * id - px; + ty = (y * a - x * c) * id - py; + dd = tx * tx + ty * ty; + if (softness != 0) { + softness *= psx * (csx + 1) / 2; + td = SQRT(dd); + sd = td - l1 - l2 * psx + softness; + if (sd > 0) { + p = MIN(1, sd / (softness * 2)) - 1; + p = (sd - softness * (1 - p * p)) / td; + tx -= p * tx; + ty -= p * ty; + dd = tx * tx + ty * ty; + } + } if (u) { float cosine, a, b; l2 *= psx; @@ -154,7 +177,7 @@ void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float if (cosine < -1) cosine = -1; else if (cosine > 1) { cosine = 1; - if (stretch && l1 + l2 > 0.0001f) sx *= (SQRT(dd) / (l1 + l2) - 1) * alpha + 1; + if (stretch) sx *= (SQRT(dd) / (l1 + l2) - 1) * alpha + 1; } a2 = ACOS(cosine) * bendDir; a = l1 + l2 * cosine; diff --git a/spine-c/spine-c/src/spine/Skeleton.c b/spine-c/spine-c/src/spine/Skeleton.c index c0a49172f..676a4e4e1 100644 --- a/spine-c/spine-c/src/spine/Skeleton.c +++ b/spine-c/spine-c/src/spine/Skeleton.c @@ -465,6 +465,7 @@ void spSkeleton_setBonesToSetupPose (const spSkeleton* self) { ikConstraint->bendDirection = ikConstraint->data->bendDirection; ikConstraint->compress = ikConstraint->data->compress; ikConstraint->stretch = ikConstraint->data->stretch; + ikConstraint->softness = ikConstraint->data->softness; ikConstraint->mix = ikConstraint->data->mix; } diff --git a/spine-c/spine-c/src/spine/SkeletonBinary.c b/spine-c/spine-c/src/spine/SkeletonBinary.c index 9ee15d847..5ecf71a8e 100644 --- a/spine-c/spine-c/src/spine/SkeletonBinary.c +++ b/spine-c/spine-c/src/spine/SkeletonBinary.c @@ -390,10 +390,11 @@ static spAnimation* _spSkeletonBinary_readAnimation (spSkeletonBinary* self, con for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) { float time = readFloat(input); float mix = readFloat(input); + float softness = readFloat(input); signed char bendDirection = readSByte(input); int compress = readBoolean(input); int stretch = readBoolean(input); - spIkConstraintTimeline_setFrame(timeline, frameIndex, time, mix, bendDirection, compress, stretch); + spIkConstraintTimeline_setFrame(timeline, frameIndex, time, mix, softness, bendDirection, compress, stretch); if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex); } spTimelineArray_add(timelines, (spTimeline*)timeline); @@ -991,6 +992,7 @@ spSkeletonData* spSkeletonBinary_readSkeletonData (spSkeletonBinary* self, const data->bones[ii] = skeletonData->bones[readVarint(input, 1)]; data->target = skeletonData->bones[readVarint(input, 1)]; data->mix = readFloat(input); + data->softness = readFloat(input); data->bendDirection = readSByte(input); data->compress = readBoolean(input); data->stretch = readBoolean(input); diff --git a/spine-c/spine-c/src/spine/SkeletonJson.c b/spine-c/spine-c/src/spine/SkeletonJson.c index bf6398d4b..1d00fba05 100644 --- a/spine-c/spine-c/src/spine/SkeletonJson.c +++ b/spine-c/spine-c/src/spine/SkeletonJson.c @@ -302,7 +302,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), + spIkConstraintTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), Json_getFloat(valueMap, "mix", 1), Json_getFloat(valueMap, "softness", 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); } @@ -753,6 +753,7 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha 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); + data->softness = Json_getFloat(constraintMap, "softness", 0); skeletonData->ikConstraints[i] = data; }