[cpp] 4.3 porting WIP

This commit is contained in:
Mario Zechner 2025-06-12 02:08:23 +02:00
parent ea8194a927
commit da36386f4a
5 changed files with 264 additions and 301 deletions

View File

@ -30,84 +30,58 @@
#ifndef Spine_PathConstraint_h #ifndef Spine_PathConstraint_h
#define Spine_PathConstraint_h #define Spine_PathConstraint_h
#include <spine/Constraint.h>
#include <spine/ConstraintData.h> #include <spine/ConstraintData.h>
#include <spine/PathConstraintData.h>
#include <spine/PathConstraintPose.h>
#include <spine/Vector.h> #include <spine/Vector.h>
namespace spine { namespace spine {
class PathConstraintData;
class Skeleton; class Skeleton;
class PathAttachment; class PathAttachment;
class BonePose;
class Bone;
class Slot; class Slot;
class Bone;
class Skin;
class Attachment;
class SP_API PathConstraint : public Updatable { /// Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the
/// constrained bones so they follow a PathAttachment.
///
/// See https://esotericsoftware.com/spine-path-constraints Path constraints in the Spine User Guide.
class SP_API PathConstraint : public Constraint<PathConstraint, PathConstraintData, PathConstraintPose> {
friend class Skeleton; friend class Skeleton;
friend class PathConstraintMixTimeline; friend class PathConstraintMixTimeline;
friend class PathConstraintPositionTimeline; friend class PathConstraintPositionTimeline;
friend class PathConstraintSpacingTimeline; friend class PathConstraintSpacingTimeline;
RTTI_DECL RTTI_DECL
public: public:
static const float epsilon;
static const int NONE, BEFORE, AFTER;
PathConstraint(PathConstraintData &data, Skeleton &skeleton); PathConstraint(PathConstraintData &data, Skeleton &skeleton);
virtual void update(Physics physics); virtual void update(Skeleton& skeleton, Physics physics);
virtual int getOrder(); virtual void sort(Skeleton& skeleton);
PathConstraintData &getData(); virtual bool isSourceActive();
Vector<Bone *> &getBones(); PathConstraintData &getData();
Slot *getTarget(); Vector<BonePose *> &getBones();
void setTarget(Slot *inValue); Slot *getSlot();
float getPosition(); void setSlot(Slot *slot);
void setPosition(float inValue);
float getSpacing();
void setSpacing(float inValue);
float getMixRotate();
void setMixRotate(float inValue);
float getMixX();
void setMixX(float inValue);
float getMixY();
void setMixY(float inValue);
bool isActive();
void setActive(bool inValue);
void setToSetupPose();
private: private:
static const float EPSILON; Vector<BonePose *> _bones;
static const int NONE; Slot *_slot;
static const int BEFORE;
static const int AFTER;
PathConstraintData &_data;
Vector<Bone *> _bones;
Slot *_target;
float _position, _spacing;
float _mixRotate, _mixX, _mixY;
Vector<float> _spaces; Vector<float> _spaces;
Vector<float> _positions; Vector<float> _positions;
@ -116,17 +90,18 @@ namespace spine {
Vector<float> _lengths; Vector<float> _lengths;
Vector<float> _segments; Vector<float> _segments;
bool _active; Vector<float> &computeWorldPositions(Skeleton& skeleton, PathAttachment &path, int spacesCount, bool tangents);
Vector<float> &computeWorldPositions(PathAttachment &path, int spacesCount, bool tangents); void addBeforePosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o);
static void addBeforePosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o); void addAfterPosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o);
static void addAfterPosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o); void addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
static void
addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
Vector<float> &output, int o, bool tangents); Vector<float> &output, int o, bool tangents);
void sortPathSlot(Skeleton& skeleton, Skin& skin, int slotIndex, Bone& slotBone);
void sortPath(Skeleton& skeleton, Attachment* attachment, Bone& slotBone);
}; };
} }

View File

@ -36,6 +36,8 @@
namespace spine { namespace spine {
/// Stores a pose for a path constraint. /// Stores a pose for a path constraint.
class SP_API PathConstraintPose : public Pose<PathConstraintPose> { class SP_API PathConstraintPose : public Pose<PathConstraintPose> {
friend class PathConstraint;
RTTI_DECL RTTI_DECL
private: private:

View File

@ -89,6 +89,8 @@ namespace spine {
friend class IkConstraintTimeline; friend class IkConstraintTimeline;
friend class PathConstraint;
friend class PathConstraintMixTimeline; friend class PathConstraintMixTimeline;
friend class PathConstraintPositionTimeline; friend class PathConstraintPositionTimeline;
@ -206,7 +208,7 @@ namespace spine {
Vector<Bone *> &getBones(); Vector<Bone *> &getBones();
Vector<Updatable *> &getUpdateCacheList(); Vector<Update *> &getUpdateCacheList();
Vector<Slot *> &getSlots(); Vector<Slot *> &getSlots();
@ -280,7 +282,7 @@ namespace spine {
Vector<TransformConstraint *> _transformConstraints; Vector<TransformConstraint *> _transformConstraints;
Vector<PathConstraint *> _pathConstraints; Vector<PathConstraint *> _pathConstraints;
Vector<PhysicsConstraint *> _physicsConstraints; Vector<PhysicsConstraint *> _physicsConstraints;
Vector<Updatable *> _updateCache; Vector<Update *> _updateCache;
Skin *_skin; Skin *_skin;
Color _color; Color _color;
float _scaleX, _scaleY; float _scaleX, _scaleY;

View File

@ -45,6 +45,7 @@ namespace spine {
friend class SkeletonBinary; friend class SkeletonBinary;
friend class SkeletonJson; friend class SkeletonJson;
friend class PathConstraint;
friend class AttachmentTimeline; friend class AttachmentTimeline;
friend class RGBATimeline; friend class RGBATimeline;
friend class RGBTimeline; friend class RGBTimeline;

View File

@ -30,259 +30,235 @@
#include <spine/PathConstraint.h> #include <spine/PathConstraint.h>
#include <spine/Bone.h> #include <spine/Bone.h>
#include <spine/BonePose.h>
#include <spine/PathAttachment.h> #include <spine/PathAttachment.h>
#include <spine/PathConstraintData.h> #include <spine/PathConstraintData.h>
#include <spine/PathConstraintPose.h>
#include <spine/Skeleton.h> #include <spine/Skeleton.h>
#include <spine/Slot.h> #include <spine/Slot.h>
#include <spine/MathUtil.h>
#include <spine/BoneData.h> #include <spine/BoneData.h>
#include <spine/SlotData.h> #include <spine/SlotData.h>
#include <spine/SkeletonData.h>
using namespace spine; using namespace spine;
RTTI_IMPL(PathConstraint, Updatable) RTTI_IMPL_NOPARENT(PathConstraint)
const float PathConstraint::EPSILON = 0.00001f; const float PathConstraint::epsilon = 0.00001f;
const int PathConstraint::NONE = -1; const int PathConstraint::NONE = -1;
const int PathConstraint::BEFORE = -2; const int PathConstraint::BEFORE = -2;
const int PathConstraint::AFTER = -3; const int PathConstraint::AFTER = -3;
PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : Updatable(), PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) :
_data(data), Constraint<PathConstraint, PathConstraintData, PathConstraintPose>(data),
_target(skeleton.findSlot( _slot(skeleton._slots.buffer()[data._slot->_index]) {
data.getTarget()->getName())),
_position(data.getPosition()), _bones.ensureCapacity(data.getBones().size());
_spacing(data.getSpacing()), for (size_t i = 0; i < data.getBones().size(); i++) {
_mixRotate(data.getMixRotate()), BoneData *boneData = data.getBones()[i];
_mixX(data.getMixX()), _bones.add(&skeleton.findBone(boneData->getName())->getAppliedPose());
_mixY(data.getMixY()),
_active(false) {
_bones.ensureCapacity(_data.getBones().size());
for (size_t i = 0; i < _data.getBones().size(); i++) {
BoneData *boneData = _data.getBones()[i];
_bones.add(skeleton.findBone(boneData->getName()));
} }
_segments.setSize(10, 0); _segments.setSize(10, 0);
} }
void PathConstraint::update(Physics) { void PathConstraint::update(Skeleton& skeleton, Physics physics) {
Attachment *baseAttachment = _target->getAttachment(); Attachment *baseAttachment = _slot->getAppliedPose().getAttachment();
if (baseAttachment == NULL || !baseAttachment->getRTTI().instanceOf(PathAttachment::rtti)) { if (baseAttachment == NULL || !baseAttachment->getRTTI().instanceOf(PathAttachment::rtti)) {
return; return;
} }
PathAttachment *attachment = static_cast<PathAttachment *>(baseAttachment); PathAttachment *pathAttachment = static_cast<PathAttachment *>(baseAttachment);
float mixRotate = _mixRotate, mixX = _mixX, mixY = _mixY; PathConstraintPose& p = *_applied;
float mixRotate = p._mixRotate, mixX = p._mixX, mixY = p._mixY;
if (mixRotate == 0 && mixX == 0 && mixY == 0) return; if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
PathConstraintData &data = _data; PathConstraintData& data = _data;
bool tangents = data._rotateMode == RotateMode_Tangent, scale = data._rotateMode == RotateMode_ChainScale; bool tangents = data.getRotateMode() == RotateMode_Tangent, scale = data.getRotateMode() == RotateMode_ChainScale;
size_t boneCount = _bones.size(); size_t boneCount = _bones.size();
size_t spacesCount = tangents ? boneCount : boneCount + 1; size_t spacesCount = tangents ? boneCount : boneCount + 1;
BonePose** bones = _bones.buffer();
_spaces.setSize(spacesCount, 0); _spaces.setSize(spacesCount, 0);
if (scale) _lengths.setSize(boneCount, 0); float* spaces = _spaces.buffer();
float spacing = _spacing; float* lengths = NULL;
if (scale) {
_lengths.setSize(boneCount, 0);
lengths = _lengths.buffer();
}
float spacing = p._spacing;
switch (data._spacingMode) { switch (data.getSpacingMode()) {
case SpacingMode_Percent: { case SpacingMode_Percent: {
if (scale) { if (scale) {
for (size_t i = 0, n = spacesCount - 1; i < n; i++) { for (size_t i = 0, n = spacesCount - 1; i < n; i++) {
Bone *boneP = _bones[i]; BonePose* bone = bones[i];
Bone &bone = *boneP; float setupLength = bone->_bone->getData().getLength();
float setupLength = bone._data.getLength(); float x = setupLength * bone->_a;
float x = setupLength * bone._a; float y = setupLength * bone->_c;
float y = setupLength * bone._c; lengths[i] = MathUtil::sqrt(x * x + y * y);
_lengths[i] = MathUtil::sqrt(x * x + y * y);
} }
} }
for (size_t i = 1; i < spacesCount; ++i) { for (size_t i = 1; i < spacesCount; i++) {
_spaces[i] = spacing; spaces[i] = spacing;
} }
break; break;
} }
case SpacingMode_Proportional: { case SpacingMode_Proportional: {
float sum = 0; float sum = 0;
for (size_t i = 0, n = spacesCount - 1; i < n;) { for (size_t i = 0, n = spacesCount - 1; i < n;) {
Bone *boneP = _bones[i]; BonePose* bone = bones[i];
Bone &bone = *boneP; float setupLength = bone->_bone->getData().getLength();
float setupLength = bone._data.getLength(); if (setupLength < epsilon) {
if (setupLength < PathConstraint::EPSILON) { if (scale) lengths[i] = 0;
if (scale) _lengths[i] = 0; spaces[++i] = spacing;
_spaces[++i] = spacing;
} else { } else {
float x = setupLength * bone._a, y = setupLength * bone._c; float x = setupLength * bone->_a, y = setupLength * bone->_c;
float length = MathUtil::sqrt(x * x + y * y); float length = MathUtil::sqrt(x * x + y * y);
if (scale) _lengths[i] = length; if (scale) lengths[i] = length;
_spaces[++i] = length; spaces[++i] = length;
sum += length; sum += length;
} }
} }
if (sum > 0) { if (sum > 0) {
sum = spacesCount / sum * spacing; sum = spacesCount / sum * spacing;
for (size_t i = 1; i < spacesCount; i++) { for (size_t i = 1; i < spacesCount; i++) {
_spaces[i] *= sum; spaces[i] *= sum;
} }
} }
break; break;
} }
default: { default: {
bool lengthSpacing = data._spacingMode == SpacingMode_Length; bool lengthSpacing = data.getSpacingMode() == SpacingMode_Length;
for (size_t i = 0, n = spacesCount - 1; i < n;) { for (size_t i = 0, n = spacesCount - 1; i < n;) {
Bone *boneP = _bones[i]; BonePose* bone = bones[i];
Bone &bone = *boneP; float setupLength = bone->_bone->getData().getLength();
float setupLength = bone._data.getLength(); if (setupLength < epsilon) {
if (setupLength < PathConstraint::EPSILON) { if (scale) lengths[i] = 0;
if (scale) _lengths[i] = 0; spaces[++i] = spacing;
_spaces[++i] = spacing;
} else { } else {
float x = setupLength * bone._a, y = setupLength * bone._c; float x = setupLength * bone->_a, y = setupLength * bone->_c;
float length = MathUtil::sqrt(x * x + y * y); float length = MathUtil::sqrt(x * x + y * y);
if (scale) _lengths[i] = length; if (scale) lengths[i] = length;
_spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; spaces[++i] = (lengthSpacing ? MathUtil::max(0.0f, setupLength + spacing) : spacing) * length / setupLength;
} }
} }
} }
} }
Vector<float> &positions = computeWorldPositions(*attachment, (int) spacesCount, tangents); Vector<float> &positions = computeWorldPositions(skeleton, *pathAttachment, (int) spacesCount, tangents);
float boneX = positions[0]; float* positionsBuffer = positions.buffer();
float boneY = positions[1]; float boneX = positionsBuffer[0], boneY = positionsBuffer[1], offsetRotation = data.getOffsetRotation();
float offsetRotation = data.getOffsetRotation();
bool tip; bool tip;
if (offsetRotation == 0) { if (offsetRotation == 0)
tip = data._rotateMode == RotateMode_Chain; tip = data.getRotateMode() == RotateMode_Chain;
} else { else {
tip = false; tip = false;
Bone &p = _target->getBone(); BonePose& bone = _slot->getBone().getAppliedPose();
offsetRotation *= p.getA() * p.getD() - p.getB() * p.getC() > 0 ? MathUtil::Deg_Rad : -MathUtil::Deg_Rad; offsetRotation *= bone._a * bone._d - bone._b * bone._c > 0 ? MathUtil::Deg_Rad : -MathUtil::Deg_Rad;
} }
for (size_t i = 0, ip = 3; i < boneCount; i++, ip += 3) {
for (size_t i = 0, p = 3; i < boneCount; i++, p += 3) { // int u = skeleton.update; // TODO: Add int update field to Skeleton class
Bone *boneP = _bones[i]; int u = 1; // Temporary placeholder until Skeleton.update is implemented
Bone &bone = *boneP; BonePose* bone = bones[i];
bone._worldX += (boneX - bone._worldX) * mixX; bone->_worldX += (boneX - bone->_worldX) * mixX;
bone._worldY += (boneY - bone._worldY) * mixY; bone->_worldY += (boneY - bone->_worldY) * mixY;
float x = positions[p]; float x = positionsBuffer[ip], y = positionsBuffer[ip + 1], dx = x - boneX, dy = y - boneY;
float y = positions[p + 1];
float dx = x - boneX;
float dy = y - boneY;
if (scale) { if (scale) {
float length = _lengths[i]; float length = lengths[i];
if (length >= PathConstraint::EPSILON) { if (length >= epsilon) {
float s = (MathUtil::sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1; float s = (MathUtil::sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
bone._a *= s; bone->_a *= s;
bone._c *= s; bone->_c *= s;
} }
} }
boneX = x; boneX = x;
boneY = y; boneY = y;
if (mixRotate > 0) { if (mixRotate > 0) {
float a = bone._a, b = bone._b, c = bone._c, d = bone._d, r, cos, sin; float a = bone->_a, b = bone->_b, c = bone->_c, d = bone->_d, r, cos, sin;
if (tangents) if (tangents)
r = positions[p - 1]; r = positionsBuffer[ip - 1];
else if (_spaces[i + 1] < PathConstraint::EPSILON) else if (spaces[i + 1] < epsilon)
r = positions[p + 2]; r = positionsBuffer[ip + 2];
else else
r = MathUtil::atan2(dy, dx); r = MathUtil::atan2(dy, dx);
r -= MathUtil::atan2(c, a); r -= MathUtil::atan2(c, a);
if (tip) { if (tip) {
cos = MathUtil::cos(r); cos = MathUtil::cos(r);
sin = MathUtil::sin(r); sin = MathUtil::sin(r);
float length = bone._data.getLength(); float length = bone->_bone->getData().getLength();
boneX += (length * (cos * a - sin * c) - dx) * mixRotate; boneX += (length * (cos * a - sin * c) - dx) * mixRotate;
boneY += (length * (sin * a + cos * c) - dy) * mixRotate; boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
} else } else
r += offsetRotation; r += offsetRotation;
if (r > MathUtil::Pi) if (r > MathUtil::Pi)
r -= MathUtil::Pi_2; r -= MathUtil::Pi_2;
else if (r < -MathUtil::Pi) else if (r < -MathUtil::Pi)
r += MathUtil::Pi_2; r += MathUtil::Pi_2;
r *= mixRotate; r *= mixRotate;
cos = MathUtil::cos(r); cos = MathUtil::cos(r);
sin = MathUtil::sin(r); sin = MathUtil::sin(r);
bone._a = cos * a - sin * c; bone->_a = cos * a - sin * c;
bone._b = cos * b - sin * d; bone->_b = cos * b - sin * d;
bone._c = sin * a + cos * c; bone->_c = sin * a + cos * c;
bone._d = sin * b + cos * d; bone->_d = sin * b + cos * d;
} }
bone->modifyWorld(u);
bone.updateAppliedTransform();
} }
} }
int PathConstraint::getOrder() {
return (int) _data.getOrder(); void PathConstraint::sort(Skeleton& skeleton) {
int slotIndex = _slot->getData().getIndex();
Bone& slotBone = _slot->getBone();
if (skeleton.getSkin() != NULL) sortPathSlot(skeleton, *skeleton.getSkin(), slotIndex, slotBone);
if (skeleton.getData()->getDefaultSkin() != NULL && skeleton.getData()->getDefaultSkin() != skeleton.getSkin())
sortPathSlot(skeleton, *skeleton.getData()->getDefaultSkin(), slotIndex, slotBone);
sortPath(skeleton, _slot->getAppliedPose().getAttachment(), slotBone);
BonePose** bones = _bones.buffer();
size_t boneCount = _bones.size();
for (size_t i = 0; i < boneCount; i++) {
Bone* bone = bones[i]->_bone;
skeleton.sortBone(bone);
// skeleton.constrained(bone); // TODO: Add constrained() method to Skeleton class
}
skeleton._updateCache.add(this);
for (size_t i = 0; i < boneCount; i++)
skeleton.sortReset(bones[i]->_bone->getChildren());
for (size_t i = 0; i < boneCount; i++)
bones[i]->_bone->_sorted = true;
} }
float PathConstraint::getPosition() { bool PathConstraint::isSourceActive() {
return _position; return _slot->getBone().isActive();
}
void PathConstraint::setPosition(float inValue) {
_position = inValue;
}
float PathConstraint::getSpacing() {
return _spacing;
}
void PathConstraint::setSpacing(float inValue) {
_spacing = inValue;
}
float PathConstraint::getMixRotate() {
return _mixRotate;
}
void PathConstraint::setMixRotate(float inValue) {
_mixRotate = inValue;
}
float PathConstraint::getMixX() {
return _mixX;
}
void PathConstraint::setMixX(float inValue) {
_mixX = inValue;
}
float PathConstraint::getMixY() {
return _mixY;
}
void PathConstraint::setMixY(float inValue) {
_mixY = inValue;
}
Vector<Bone *> &PathConstraint::getBones() {
return _bones;
}
Slot *PathConstraint::getTarget() {
return _target;
}
void PathConstraint::setTarget(Slot *inValue) {
_target = inValue;
} }
PathConstraintData &PathConstraint::getData() { PathConstraintData &PathConstraint::getData() {
return _data; return _data;
} }
Vector<BonePose *> &PathConstraint::getBones() {
return _bones;
}
Slot *PathConstraint::getSlot() {
return _slot;
}
void PathConstraint::setSlot(Slot *slot) {
if (slot == NULL) throw;
_slot = slot;
}
Vector<float> & Vector<float> &
PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, bool tangents) { PathConstraint::computeWorldPositions(Skeleton& skeleton, PathAttachment &path, int spacesCount, bool tangents) {
Slot &target = *_target; Slot &slot = *_slot;
float position = _position; float position = _applied->_position;
float* spaces = _spaces.buffer();
_positions.setSize(spacesCount * 3 + 2, 0); _positions.setSize(spacesCount * 3 + 2, 0);
Vector<float> &out = _positions; Vector<float> &out = _positions;
float* outBuffer = out.buffer();
Vector<float> &world = _world; Vector<float> &world = _world;
bool closed = path.isClosed(); bool closed = path.isClosed();
int verticesLength = (int) path.getWorldVerticesLength(); int verticesLength = (int) path.getWorldVerticesLength();
@ -292,12 +268,13 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
float pathLength; float pathLength;
if (!path.isConstantSpeed()) { if (!path.isConstantSpeed()) {
Vector<float> &lengths = path.getLengths(); Vector<float> &lengths = path.getLengths();
float* lengthsBuffer = lengths.buffer();
curveCount -= closed ? 1 : 2; curveCount -= closed ? 1 : 2;
pathLength = lengths[curveCount]; pathLength = lengthsBuffer[curveCount];
if (_data._positionMode == PositionMode_Percent) position *= pathLength; if (_data.getPositionMode() == PositionMode_Percent) position *= pathLength;
float multiplier = 0; float multiplier = 0;
switch (_data._spacingMode) { switch (_data.getSpacingMode()) {
case SpacingMode_Percent: case SpacingMode_Percent:
multiplier = pathLength; multiplier = pathLength;
break; break;
@ -309,8 +286,9 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
} }
world.setSize(8, 0); world.setSize(8, 0);
float* worldBuffer = world.buffer();
for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
float space = _spaces[i] * multiplier; float space = spaces[i] * multiplier;
position += space; position += space;
float p = position; float p = position;
@ -321,48 +299,41 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
} else if (p < 0) { } else if (p < 0) {
if (prevCurve != BEFORE) { if (prevCurve != BEFORE) {
prevCurve = BEFORE; prevCurve = BEFORE;
path.computeWorldVertices(target, 2, 4, world, 0); path.computeWorldVertices(skeleton, *_slot, 2, 4, world, 0, 2);
} }
addBeforePosition(p, world, 0, out, o); addBeforePosition(p, world, 0, out, o);
continue; continue;
} else if (p > pathLength) { } else if (p > pathLength) {
if (prevCurve != AFTER) { if (prevCurve != AFTER) {
prevCurve = AFTER; prevCurve = AFTER;
path.computeWorldVertices(target, verticesLength - 6, 4, world, 0); path.computeWorldVertices(skeleton, *_slot, verticesLength - 6, 4, world, 0, 2);
} }
addAfterPosition(p - pathLength, world, 0, out, o); addAfterPosition(p - pathLength, world, 0, out, o);
continue; continue;
} }
// Determine curve containing position. // Determine curve containing position.
for (;; curve++) { for (;; curve++) {
float length = lengths[curve]; float length = lengthsBuffer[curve];
if (p > length) continue; if (p > length) continue;
if (curve == 0) if (curve == 0)
p /= length; p /= length;
else { else {
float prev = lengths[curve - 1]; float prev = lengthsBuffer[curve - 1];
p = (p - prev) / (length - prev); p = (p - prev) / (length - prev);
} }
break; break;
} }
if (curve != prevCurve) { if (curve != prevCurve) {
prevCurve = curve; prevCurve = curve;
if (closed && curve == curveCount) { if (closed && curve == curveCount) {
path.computeWorldVertices(target, verticesLength - 4, 4, world, 0); path.computeWorldVertices(skeleton, *_slot, verticesLength - 4, 4, world, 0, 2);
path.computeWorldVertices(target, 0, 4, world, 4); path.computeWorldVertices(skeleton, *_slot, 0, 4, world, 4, 2);
} else } else
path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0); path.computeWorldVertices(skeleton, *_slot, curve * 6 + 2, 8, world, 0, 2);
} }
addCurvePosition(p, worldBuffer[0], worldBuffer[1], worldBuffer[2], worldBuffer[3], worldBuffer[4], worldBuffer[5], worldBuffer[6], worldBuffer[7],
addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space < epsilon));
out, o, tangents || (i > 0 && space < EPSILON));
} }
return out; return out;
} }
@ -371,29 +342,32 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
if (closed) { if (closed) {
verticesLength += 2; verticesLength += 2;
world.setSize(verticesLength, 0); world.setSize(verticesLength, 0);
path.computeWorldVertices(target, 2, verticesLength - 4, world, 0); float* worldBuffer = world.buffer();
path.computeWorldVertices(target, 0, 2, world, verticesLength - 4); path.computeWorldVertices(skeleton, *_slot, 2, verticesLength - 4, world, 0, 2);
world[verticesLength - 2] = world[0]; path.computeWorldVertices(skeleton, *_slot, 0, 2, world, verticesLength - 4, 2);
world[verticesLength - 1] = world[1]; worldBuffer[verticesLength - 2] = worldBuffer[0];
worldBuffer[verticesLength - 1] = worldBuffer[1];
} else { } else {
curveCount--; curveCount--;
verticesLength -= 4; verticesLength -= 4;
world.setSize(verticesLength, 0); world.setSize(verticesLength, 0);
path.computeWorldVertices(target, 2, verticesLength, world, 0); path.computeWorldVertices(skeleton, *_slot, 2, verticesLength, world, 0, 2);
} }
float* worldBuffer = world.buffer();
// Curve lengths. // Curve lengths.
_curves.setSize(curveCount, 0); _curves.setSize(curveCount, 0);
float* curvesBuffer = _curves.buffer();
pathLength = 0; pathLength = 0;
float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; float x1 = worldBuffer[0], y1 = worldBuffer[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy; float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
for (int i = 0, w = 2; i < curveCount; i++, w += 6) { for (int i = 0, w = 2; i < curveCount; i++, w += 6) {
cx1 = world[w]; cx1 = worldBuffer[w];
cy1 = world[w + 1]; cy1 = worldBuffer[w + 1];
cx2 = world[w + 2]; cx2 = worldBuffer[w + 2];
cy2 = world[w + 3]; cy2 = worldBuffer[w + 3];
x2 = world[w + 4]; x2 = worldBuffer[w + 4];
y2 = world[w + 5]; y2 = worldBuffer[w + 5];
tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f; tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f;
tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f; dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
@ -414,15 +388,15 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
dfx += ddfx + dddfx; dfx += ddfx + dddfx;
dfy += ddfy + dddfy; dfy += ddfy + dddfy;
pathLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); pathLength += MathUtil::sqrt(dfx * dfx + dfy * dfy);
_curves[i] = pathLength; curvesBuffer[i] = pathLength;
x1 = x2; x1 = x2;
y1 = y2; y1 = y2;
} }
if (_data._positionMode == PositionMode_Percent) position *= pathLength; if (_data.getPositionMode() == PositionMode_Percent) position *= pathLength;
float multiplier = 0; float multiplier = 0;
switch (_data._spacingMode) { switch (_data.getSpacingMode()) {
case SpacingMode_Percent: case SpacingMode_Percent:
multiplier = pathLength; multiplier = pathLength;
break; break;
@ -432,10 +406,11 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
default: default:
multiplier = 1; multiplier = 1;
} }
float* segmentsBuffer = _segments.buffer();
float curveLength = 0; float curveLength = 0;
for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
float space = _spaces[i] * multiplier; float space = spaces[i] * multiplier;
position += space; position += space;
float p = position; float p = position;
@ -443,6 +418,7 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
p = MathUtil::fmod(p, pathLength); p = MathUtil::fmod(p, pathLength);
if (p < 0) p += pathLength; if (p < 0) p += pathLength;
curve = 0; curve = 0;
segment = 0;
} else if (p < 0) { } else if (p < 0) {
addBeforePosition(p, world, 0, out, o); addBeforePosition(p, world, 0, out, o);
continue; continue;
@ -453,12 +429,12 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
// Determine curve containing position. // Determine curve containing position.
for (;; curve++) { for (;; curve++) {
float length = _curves[curve]; float length = curvesBuffer[curve];
if (p > length) continue; if (p > length) continue;
if (curve == 0) if (curve == 0)
p /= length; p /= length;
else { else {
float prev = _curves[curve - 1]; float prev = curvesBuffer[curve - 1];
p = (p - prev) / (length - prev); p = (p - prev) / (length - prev);
} }
break; break;
@ -468,14 +444,14 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
if (curve != prevCurve) { if (curve != prevCurve) {
prevCurve = curve; prevCurve = curve;
int ii = curve * 6; int ii = curve * 6;
x1 = world[ii]; x1 = worldBuffer[ii];
y1 = world[ii + 1]; y1 = worldBuffer[ii + 1];
cx1 = world[ii + 2]; cx1 = worldBuffer[ii + 2];
cy1 = world[ii + 3]; cy1 = worldBuffer[ii + 3];
cx2 = world[ii + 4]; cx2 = worldBuffer[ii + 4];
cy2 = world[ii + 5]; cy2 = worldBuffer[ii + 5];
x2 = world[ii + 6]; x2 = worldBuffer[ii + 6];
y2 = world[ii + 7]; y2 = worldBuffer[ii + 7];
tmpx = (x1 - cx1 * 2 + cx2) * 0.03f; tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f; dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
@ -485,104 +461,111 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f; dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
curveLength = MathUtil::sqrt(dfx * dfx + dfy * dfy); curveLength = MathUtil::sqrt(dfx * dfx + dfy * dfy);
_segments[0] = curveLength; segmentsBuffer[0] = curveLength;
for (ii = 1; ii < 8; ii++) { for (ii = 1; ii < 8; ii++) {
dfx += ddfx; dfx += ddfx;
dfy += ddfy; dfy += ddfy;
ddfx += dddfx; ddfx += dddfx;
ddfy += dddfy; ddfy += dddfy;
curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy);
_segments[ii] = curveLength; segmentsBuffer[ii] = curveLength;
} }
dfx += ddfx; dfx += ddfx;
dfy += ddfy; dfy += ddfy;
curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy);
_segments[8] = curveLength; segmentsBuffer[8] = curveLength;
dfx += ddfx + dddfx; dfx += ddfx + dddfx;
dfy += ddfy + dddfy; dfy += ddfy + dddfy;
curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy); curveLength += MathUtil::sqrt(dfx * dfx + dfy * dfy);
_segments[9] = curveLength; segmentsBuffer[9] = curveLength;
segment = 0; segment = 0;
} }
// Weight by segment length. // Weight by segment length.
p *= curveLength; p *= curveLength;
for (;; segment++) { for (;; segment++) {
float length = _segments[segment]; float length = segmentsBuffer[segment];
if (p > length) continue; if (p > length) continue;
if (segment == 0) if (segment == 0)
p /= length; p /= length;
else { else {
float prev = _segments[segment - 1]; float prev = segmentsBuffer[segment - 1];
p = segment + (p - prev) / (length - prev); p = segment + (p - prev) / (length - prev);
} }
break; break;
} }
addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o,
tangents || (i > 0 && space < EPSILON)); tangents || (i > 0 && space < epsilon));
} }
return out; return out;
} }
void PathConstraint::addBeforePosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o) { void PathConstraint::addBeforePosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o) {
float x1 = temp[i]; float* tempBuffer = temp.buffer();
float y1 = temp[i + 1]; float* outBuffer = output.buffer();
float dx = temp[i + 2] - x1; float x1 = tempBuffer[i], y1 = tempBuffer[i + 1], dx = tempBuffer[i + 2] - x1, dy = tempBuffer[i + 3] - y1, r = MathUtil::atan2(dy, dx);
float dy = temp[i + 3] - y1; outBuffer[o] = x1 + p * MathUtil::cos(r);
float r = MathUtil::atan2(dy, dx); outBuffer[o + 1] = y1 + p * MathUtil::sin(r);
output[o] = x1 + p * MathUtil::cos(r); outBuffer[o + 2] = r;
output[o + 1] = y1 + p * MathUtil::sin(r);
output[o + 2] = r;
} }
void PathConstraint::addAfterPosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o) { void PathConstraint::addAfterPosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o) {
float x1 = temp[i + 2]; float* tempBuffer = temp.buffer();
float y1 = temp[i + 3]; float* outBuffer = output.buffer();
float dx = x1 - temp[i]; float x1 = tempBuffer[i + 2], y1 = tempBuffer[i + 3], dx = x1 - tempBuffer[i], dy = y1 - tempBuffer[i + 1], r = MathUtil::atan2(dy, dx);
float dy = y1 - temp[i + 1]; outBuffer[o] = x1 + p * MathUtil::cos(r);
float r = MathUtil::atan2(dy, dx); outBuffer[o + 1] = y1 + p * MathUtil::sin(r);
output[o] = x1 + p * MathUtil::cos(r); outBuffer[o + 2] = r;
output[o + 1] = y1 + p * MathUtil::sin(r);
output[o + 2] = r;
} }
void PathConstraint::addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, void PathConstraint::addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2,
float y2, Vector<float> &output, int o, bool tangents) { float y2, Vector<float> &output, int o, bool tangents) {
if (p < EPSILON || MathUtil::isNan(p)) { float* outBuffer = output.buffer();
output[o] = x1; if (p < epsilon || MathUtil::isNan(p)) {
output[o + 1] = y1; outBuffer[o] = x1;
output[o + 2] = MathUtil::atan2(cy1 - y1, cx1 - x1); outBuffer[o + 1] = y1;
outBuffer[o + 2] = MathUtil::atan2(cy1 - y1, cx1 - x1);
return; return;
} }
float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
output[o] = x; outBuffer[o] = x;
output[o + 1] = y; outBuffer[o + 1] = y;
if (tangents) { if (tangents) {
if (p < 0.001) if (p < 0.001f)
output[o + 2] = MathUtil::atan2(cy1 - y1, cx1 - x1); outBuffer[o + 2] = MathUtil::atan2(cy1 - y1, cx1 - x1);
else else
output[o + 2] = MathUtil::atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), outBuffer[o + 2] = MathUtil::atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt),
x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
} }
} }
bool PathConstraint::isActive() { void PathConstraint::sortPathSlot(Skeleton& skeleton, Skin& skin, int slotIndex, Bone& slotBone) {
return _active; // Object[] entries = skin.attachments.orderedItems().items;
// for (int i = 0, n = skin.attachments.size; i < n; i++) {
// var entry = (SkinEntry)entries[i];
// if (entry.slotIndex == slotIndex) sortPath(skeleton, entry.attachment, slotBone);
// }
// TODO: Implement when Skin API is available
} }
void PathConstraint::setActive(bool inValue) { void PathConstraint::sortPath(Skeleton& skeleton, Attachment* attachment, Bone& slotBone) {
_active = inValue; if (attachment == NULL || !attachment->getRTTI().instanceOf(PathAttachment::rtti)) return;
} PathAttachment* pathAttachment = static_cast<PathAttachment*>(attachment);
// int[] pathBones = pathAttachment.getBones();
void PathConstraint::setToSetupPose() { // if (pathBones == null)
PathConstraintData &data = this->_data; // skeleton.sortBone(slotBone);
this->_position = data._position; // else {
this->_spacing = data._spacing; // Bone[] bones = skeleton.bones.items;
this->_mixRotate = data._mixRotate; // for (int i = 0, n = pathBones.length; i < n;) {
this->_mixX = data._mixX; // int nn = pathBones[i++];
this->_mixY = data._mixY; // nn += i;
// while (i < nn)
// skeleton.sortBone(bones[pathBones[i++]]);
// }
// }
// TODO: Implement when PathAttachment::getBones() API is available
skeleton.sortBone(&slotBone);
} }