[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
#define Spine_PathConstraint_h
#include <spine/Constraint.h>
#include <spine/ConstraintData.h>
#include <spine/PathConstraintData.h>
#include <spine/PathConstraintPose.h>
#include <spine/Vector.h>
namespace spine {
class PathConstraintData;
class Skeleton;
class PathAttachment;
class Bone;
class BonePose;
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 PathConstraintMixTimeline;
friend class PathConstraintPositionTimeline;
friend class PathConstraintSpacingTimeline;
RTTI_DECL
RTTI_DECL
public:
static const float epsilon;
static const int NONE, BEFORE, AFTER;
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:
static const float EPSILON;
static const int NONE;
static const int BEFORE;
static const int AFTER;
PathConstraintData &_data;
Vector<Bone *> _bones;
Slot *_target;
float _position, _spacing;
float _mixRotate, _mixX, _mixY;
Vector<BonePose *> _bones;
Slot *_slot;
Vector<float> _spaces;
Vector<float> _positions;
@ -116,17 +90,18 @@ namespace spine {
Vector<float> _lengths;
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);
static void
addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
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);
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 {
/// Stores a pose for a path constraint.
class SP_API PathConstraintPose : public Pose<PathConstraintPose> {
friend class PathConstraint;
RTTI_DECL
private:

View File

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

View File

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

View File

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