Эх сурвалжийг харах

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

badlogic 6 жил өмнө
parent
commit
58e2971d81

+ 6 - 1
spine-cpp/spine-cpp/include/spine/IkConstraint.h

@@ -56,7 +56,7 @@ public:
 	/// 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.
 	/// @param child A direct descendant of the parent bone.
-	static void apply(Bone &parent, Bone &child, float targetX, float targetY, int bendDir, bool stretch, float alpha);
+	static void apply(Bone &parent, Bone &child, float targetX, float targetY, int bendDir, bool stretch, float softness, float alpha);
 
 	IkConstraint(IkConstraintData &data, Skeleton &skeleton);
 
@@ -91,6 +91,10 @@ public:
 
 	void setMix(float inValue);
 
+	float getSoftness();
+
+	void setSoftness(float inValue);
+
 	bool isActive();
 
 	void setActive(bool inValue);
@@ -102,6 +106,7 @@ private:
 	bool _compress;
 	bool _stretch;
 	float _mix;
+	float _softness;
 	Bone *_target;
 	bool _active;
 };

+ 4 - 0
spine-cpp/spine-cpp/include/spine/IkConstraintData.h

@@ -71,6 +71,9 @@ namespace spine {
         float getMix();
         void setMix(float inValue);
 
+		float getSoftness();
+		void setSoftness(float inValue);
+
     private:
         Vector<BoneData*> _bones;
         BoneData* _target;
@@ -79,6 +82,7 @@ namespace spine {
         bool _stretch;
         bool _uniform;
         float _mix;
+        float _softness;
     };
 }
 

+ 3 - 1
spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h

@@ -50,15 +50,17 @@ 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 compress, bool stretch);
+        void setFrame (int frameIndex, float time, float mix, float softness, int bendDirection, bool compress, bool stretch);
         
     private:
 		static const int PREV_TIME;
 		static const int PREV_MIX;
+		static const int PREV_SOFTNESS;
 		static const int PREV_BEND_DIRECTION;
 		static const int PREV_COMPRESS;
 		static const int PREV_STRETCH;
 		static const int MIX;
+		static const int SOFTNESS;
 		static const int BEND_DIRECTION;
 		static const int COMPRESS;
 		static const int STRETCH;

+ 34 - 13
spine-cpp/spine-cpp/src/spine/IkConstraint.cpp

@@ -69,12 +69,13 @@ void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool compress
 							  sy, bone._ashearX, bone._ashearY);
 }
 
-void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY, int bendDir, bool stretch, float alpha) {
+void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY, int bendDir, bool 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;
 	Bone *pp = parent.getParent();
-	float tx, ty, dx, dy, dd, l1, l2, a1, a2, r;
+	float tx, ty, dx, dy, dd, l1, l2, a1, a2, r, td, sd, p;
 	float id, x, y;
 	if (alpha == 0) {
 		child.updateWorldTransform();
@@ -117,18 +118,37 @@ void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY
 		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 = MathUtil::sqrt(dx * dx + dy * dy);
-	l2 = child.getData().getLength() * csx;
+	l2 = child._data.getLength() * csx, a1, a2;
+	if (l1 < 0.0001) {
+		apply(parent, targetX, targetY, false, stretch, false, alpha);
+		child.updateWorldTransform(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 = MathUtil::sqrt(dd), sd = td - l1 - l2 * psx + softness;
+		if (sd > 0) {
+			p = MathUtil::min(1.0f, 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;
@@ -136,7 +156,7 @@ void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY
 		if (cosine < -1) cosine = -1;
 		else if (cosine > 1) {
 			cosine = 1;
-			if (stretch && l1 + l2 > 0.0001f) sx *= (MathUtil::sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
+			if (stretch) sx *= (MathUtil::sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
 		}
 		a2 = MathUtil::acos(cosine) * bendDir;
 		a = l1 + l2 * cosine;
@@ -213,6 +233,7 @@ IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Updatab
 																		 _compress(data.getCompress()),
 																		 _stretch(data.getStretch()),
 																		 _mix(data.getMix()),
+																		 _softness(data.getSoftness()),
 																		 _target(skeleton.findBone(
 																				 data.getTarget()->getName())),
 																				 _active(false) {
@@ -238,7 +259,7 @@ void IkConstraint::update() {
 		case 2: {
 			Bone *bone0 = _bones[0];
 			Bone *bone1 = _bones[1];
-			apply(*bone0, *bone1, _target->getWorldX(), _target->getWorldY(), _bendDirection, _stretch, _mix);
+			apply(*bone0, *bone1, _target->getWorldX(), _target->getWorldY(), _bendDirection, _stretch, _softness, _mix);
 		}
 			break;
 	}

+ 11 - 1
spine-cpp/spine-cpp/src/spine/IkConstraintData.cpp

@@ -44,7 +44,8 @@ IkConstraintData::IkConstraintData(const String &name) :
 		_compress(false),
 		_stretch(false),
 		_uniform(false),
-		_mix(1) {
+		_mix(1),
+		_softness(0) {
 }
 
 Vector<BoneData *> &IkConstraintData::getBones() {
@@ -99,3 +100,12 @@ bool IkConstraintData::getUniform() {
 void IkConstraintData::setUniform(bool inValue) {
 	_uniform = inValue;
 }
+
+float IkConstraintData::getSoftness() {
+	return _softness;
+}
+
+void IkConstraintData::setSoftness(float inValue) {
+	_softness = inValue;
+}
+

+ 19 - 7
spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp

@@ -47,16 +47,18 @@ using namespace spine;
 
 RTTI_IMPL(IkConstraintTimeline, CurveTimeline)
 
-const int IkConstraintTimeline::ENTRIES = 5;
-const int IkConstraintTimeline::PREV_TIME = -5;
-const int IkConstraintTimeline::PREV_MIX = -4;
+const int IkConstraintTimeline::ENTRIES = 6;
+const int IkConstraintTimeline::PREV_TIME = -6;
+const int IkConstraintTimeline::PREV_MIX = -5;
+const int IkConstraintTimeline::PREV_SOFTNESS = -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::COMPRESS = 3;
-const int IkConstraintTimeline::STRETCH = 4;
+const int IkConstraintTimeline::SOFTNESS = 2;
+const int IkConstraintTimeline::BEND_DIRECTION = 3;
+const int IkConstraintTimeline::COMPRESS = 4;
+const int IkConstraintTimeline::STRETCH = 5;
 
 IkConstraintTimeline::IkConstraintTimeline(int frameCount) : CurveTimeline(frameCount), _ikConstraintIndex(0) {
 	_frames.setSize(frameCount * ENTRIES, 0);
@@ -75,12 +77,14 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 		switch (blend) {
 			case MixBlend_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 MixBlend_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;
@@ -95,6 +99,8 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 		if (blend == MixBlend_Setup) {
 			constraint._mix =
 					constraint._data._mix + (_frames[_frames.size() + PREV_MIX] - constraint._data._mix) * alpha;
+			constraint._softness = constraint._data._softness
+								  + (_frames[_frames.size() + PREV_SOFTNESS] - constraint._data._softness) * alpha;
 			if (direction == MixDirection_Out) {
 				constraint._bendDirection = constraint._data._bendDirection;
 				constraint._compress = constraint._data._compress;
@@ -106,6 +112,7 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 			}
 		} else {
 			constraint._mix += (_frames[_frames.size() + PREV_MIX] - constraint._mix) * alpha;
+			constraint._softness += (_frames[_frames.size() + PREV_SOFTNESS] - constraint._softness) * alpha;
 			if (direction == MixDirection_In) {
 				constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION];
 				constraint._compress = _frames[_frames.size() + PREV_COMPRESS] != 0;
@@ -118,6 +125,7 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 	// Interpolate between the previous frame and the current frame.
 	int frame = Animation::binarySearch(_frames, time, ENTRIES);
 	float mix = _frames[frame + PREV_MIX];
+	float softness = _frames[frame + PREV_SOFTNESS];
 	float frameTime = _frames[frame];
 	float percent = getCurvePercent(frame / ENTRIES - 1,
 									1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
@@ -125,6 +133,8 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 	if (blend == MixBlend_Setup) {
 		constraint._mix =
 				constraint._data._mix + (mix + (_frames[frame + MIX] - mix) * percent - constraint._data._mix) * alpha;
+		constraint._softness = constraint._data._softness
+							  + (softness + (_frames[frame + SOFTNESS] - softness) * percent - constraint._data._softness) * alpha;
 		if (direction == MixDirection_Out) {
 			constraint._bendDirection = constraint._data._bendDirection;
 			constraint._compress = constraint._data._compress;
@@ -136,6 +146,7 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 		}
 	} else {
 		constraint._mix += (mix + (_frames[frame + MIX] - mix) * percent - constraint._mix) * alpha;
+		constraint._softness += (softness + (_frames[frame + SOFTNESS] - softness) * percent - constraint._softness) * alpha;
 		if (direction == MixDirection_In) {
 			constraint._bendDirection = (int) _frames[frame + PREV_BEND_DIRECTION];
 			constraint._compress = _frames[frame + PREV_COMPRESS] != 0;
@@ -148,10 +159,11 @@ int IkConstraintTimeline::getPropertyId() {
 	return ((int) TimelineType_IkConstraint << 24) + _ikConstraintIndex;
 }
 
-void IkConstraintTimeline::setFrame(int frameIndex, float time, float mix, int bendDirection, bool compress, bool stretch) {
+void IkConstraintTimeline::setFrame(int frameIndex, float time, float mix, float softness, int bendDirection, bool compress, bool stretch) {
 	frameIndex *= ENTRIES;
 	_frames[frameIndex] = time;
 	_frames[frameIndex + MIX] = mix;
+	_frames[frameIndex + SOFTNESS] = softness;
 	_frames[frameIndex + BEND_DIRECTION] = (float)bendDirection;
 	_frames[frameIndex + COMPRESS] = compress ? 1 : 0;
 	_frames[frameIndex + STRETCH] = stretch ? 1 : 0;

+ 1 - 0
spine-cpp/spine-cpp/src/spine/Skeleton.cpp

@@ -247,6 +247,7 @@ void Skeleton::setBonesToSetupPose() {
 		constraint._compress = constraint._data._compress;
 		constraint._stretch = constraint._data._stretch;
 		constraint._mix = constraint._data._mix;
+		constraint._softness = constraint._data._softness;
 	}
 
 	for (size_t i = 0, n = _transformConstraints.size(); i < n; ++i) {

+ 3 - 1
spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp

@@ -209,6 +209,7 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 		}
 		data->_target = skeletonData->_bones[readVarint(input, true)];
 		data->_mix = readFloat(input);
+		data->_softness = readFloat(input);
 		data->_bendDirection = readSByte(input);
 		data->_compress = readBoolean(input);
 		data->_stretch = readBoolean(input);
@@ -809,10 +810,11 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 		for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
 			float time = readFloat(input);
 			float mix = readFloat(input);
+			float softness = readFloat(input);
 			signed char bendDirection = readSByte(input);
 			bool compress = readBoolean(input);
 			bool stretch = readBoolean(input);
-			timeline->setFrame(frameIndex, time, mix, bendDirection, compress, stretch);
+			timeline->setFrame(frameIndex, time, mix, softness, bendDirection, compress, stretch);
 			if (frameIndex < frameCount - 1) {
 				readCurve(input, frameIndex, timeline);
 			}

+ 2 - 1
spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp

@@ -292,6 +292,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 			}
 
 			data->_mix = Json::getFloat(constraintMap, "mix", 1);
+			data->_softness = Json::getFloat(constraintMap, "softness", 0);
 			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;
@@ -1004,7 +1005,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),
+			timeline->setFrame(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) ? true : false, Json::getInt(valueMap, "stretch", 0) ? true : false);
 			readCurve(valueMap, timeline, frameIndex);
 		}