[c] Added soft IK support. See #1383.

This commit is contained in:
badlogic 2019-06-19 16:13:09 +02:00
parent fbee0faf82
commit 7a19650b5b
8 changed files with 63 additions and 22 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}