This commit is contained in:
NathanSweet 2013-02-22 23:37:36 +01:00
parent 6710c1e9c9
commit 31b07f8625
15 changed files with 156 additions and 39 deletions

View File

@ -274,5 +274,27 @@
{ "time": 1.0666, "angle": 3.6 }
]
}
},
"slots": {
"torso": {
"attachment": [
{ "time": 0.4333, "name": null },
{ "time": 0.8, "name": "torso" }
]
},
"head": {
"color": [
{ "time": 0.1666, "color": "ffffffff" },
{ "time": 0.9333, "color": "ff0f00c1" }
]
},
"eyes": {
"attachment": [
{ "time": 0.2, "name": "eyes-closed" },
{ "time": 0.3, "name": "eyes" },
{ "time": 0.7, "name": "eyes-closed" },
{ "time": 0.8333, "name": "eyes" }
]
}
}
}

View File

@ -133,7 +133,8 @@ public:
virtual int getKeyframeCount ();
virtual void apply (BaseSkeleton *skeleton, float time, float alpha);
void setKeyframe (int keyframeIndex, float time, std::string *attachmentName);
/** @param attachmentName Pass an empty string to clear the image for a slot. */
void setKeyframe (int keyframeIndex, float time, const std::string &attachmentName);
};
} /* namespace spine */

View File

@ -13,6 +13,8 @@ public:
float x, y, scaleX, scaleY, rotation, width, height;
float offset[8];
BaseRegionAttachment ();
void updateOffset ();
virtual void updateWorldVertices (Bone *bone) = 0;

View File

@ -13,6 +13,7 @@ class BaseSkeletonJson {
public:
BaseAttachmentLoader *attachmentLoader;
float scale;
bool flipY;
BaseSkeletonJson (BaseAttachmentLoader *attachmentLoader);
virtual ~BaseSkeletonJson ();

View File

@ -13,12 +13,18 @@ public:
float x, y;
float rotation;
float scaleX, scaleY;
float flipY;
BoneData (const std::string &name) :
name(name),
parent(0),
length(0),
x(0),
y(0),
rotation(0),
scaleX(1),
scaleY(1) {
scaleY(1),
flipY(false) {
}
};

View File

@ -21,6 +21,8 @@ int main () {
Animation *animation = skeletonJson.readAnimation(animationFile, skeletonData);
Skeleton *skeleton = new Skeleton(skeletonData);
skeleton->flipX = false;
skeleton->flipY = false;
skeleton->setToBindPose();
skeleton->getRootBone()->x = 200;
skeleton->getRootBone()->y = 420;
@ -42,9 +44,6 @@ int main () {
deltaClock.restart();
animationTime += delta;
skeleton->setToBindPose();
skeleton->getRootBone()->x = 200;
skeleton->getRootBone()->y = 420;
animation->apply(skeleton, animationTime, true);
skeleton->updateWorldTransform();
}

View File

@ -17,7 +17,6 @@ Skeleton::Skeleton (SkeletonData *skeletonData) :
BaseSkeleton(skeletonData),
vertexArray(Quads, skeletonData->bones.size() * 4),
texture(0) {
flipY = true; // BOZO - Flip in loader for animation?
}
void Skeleton::draw (RenderTarget& target, RenderStates states) const {

View File

@ -5,10 +5,12 @@ namespace spine {
SkeletonJson::SkeletonJson (BaseAttachmentLoader *attachmentLoader) :
BaseSkeletonJson(attachmentLoader) {
flipY = true;
}
SkeletonJson::SkeletonJson (Atlas *atlas) :
BaseSkeletonJson(new AtlasAttachmentLoader(atlas)) {
flipY = true;
}
} /* namespace spine */

View File

@ -104,7 +104,7 @@ float CurveTimeline::getCurvePercent (int keyframeIndex, float percent) {
//
/** @param target After the first and before the last entry. */
int binarySearch (float *values, int valuesLength, float target, int step) {
static int binarySearch (float *values, int valuesLength, float target, int step) {
int low = 0;
int high = valuesLength / step - 2;
if (high == 0) return step;
@ -120,15 +120,15 @@ int binarySearch (float *values, int valuesLength, float target, int step) {
return 0;
}
int linearSearch (float *values, int valuesLength, float target, int step) {
for (int i = 0, last = valuesLength - step; i <= last; i += step) {
if (values[i] <= target) continue;
return i;
}
return -1;
}
//
/*
static int linearSearch (float *values, int valuesLength, float target, int step) {
for (int i = 0, last = valuesLength - step; i <= last; i += step) {
if (values[i] <= target) continue;
return i;
}
return -1;
}
*/
static const int ROTATE_LAST_FRAME_TIME = -2;
static const int ROTATE_FRAME_VALUE = 1;
@ -138,6 +138,7 @@ RotateTimeline::RotateTimeline (int keyframeCount) :
framesLength(keyframeCount * 2),
frames(new float[framesLength]),
boneIndex(0) {
memset(frames, 0, sizeof(float) * framesLength);
}
RotateTimeline::~RotateTimeline () {
@ -174,7 +175,7 @@ void RotateTimeline::apply (BaseSkeleton *skeleton, float time, float alpha) {
}
// Interpolate between the last frame and the current frame.
int frameIndex = linearSearch(frames, framesLength, time, 2);
int frameIndex = binarySearch(frames, framesLength, time, 2);
float lastFrameValue = frames[frameIndex - 1];
float frameTime = frames[frameIndex];
float percent = 1 - (time - frameTime) / (frames[frameIndex + ROTATE_LAST_FRAME_TIME] - frameTime);
@ -208,6 +209,7 @@ TranslateTimeline::TranslateTimeline (int keyframeCount) :
framesLength(keyframeCount * 3),
frames(new float[framesLength]),
boneIndex(0) {
memset(frames, 0, sizeof(float) * framesLength);
}
TranslateTimeline::~TranslateTimeline () {
@ -260,7 +262,6 @@ void TranslateTimeline::apply (BaseSkeleton *skeleton, float time, float alpha)
ScaleTimeline::ScaleTimeline (int keyframeCount) :
TranslateTimeline(keyframeCount) {
frames = new float[framesLength];
}
void ScaleTimeline::apply (BaseSkeleton *skeleton, float time, float alpha) {
@ -304,6 +305,7 @@ ColorTimeline::ColorTimeline (int keyframeCount) :
framesLength(keyframeCount * 5),
frames(new float[framesLength]),
slotIndex(0) {
memset(frames, 0, sizeof(float) * framesLength);
}
ColorTimeline::~ColorTimeline () {
@ -379,6 +381,8 @@ AttachmentTimeline::AttachmentTimeline (int keyframeCount) :
frames(new float[keyframeCount]),
attachmentNames(new string*[keyframeCount]),
slotIndex(0) {
memset(frames, 0, sizeof(float) * keyframeCount);
memset(attachmentNames, 0, sizeof(string*) * keyframeCount);
}
AttachmentTimeline::~AttachmentTimeline () {
@ -397,10 +401,10 @@ int AttachmentTimeline::getKeyframeCount () {
return framesLength;
}
void AttachmentTimeline::setKeyframe (int keyframeIndex, float time, string *attachmentName) {
void AttachmentTimeline::setKeyframe (int keyframeIndex, float time, const string &attachmentName) {
frames[keyframeIndex] = time;
if (attachmentNames[keyframeIndex]) delete attachmentNames[keyframeIndex];
attachmentNames[keyframeIndex] = attachmentName ? new string(*attachmentName) : 0;
attachmentNames[keyframeIndex] = attachmentName.length() == 0 ? 0 : new string(attachmentName);
}
void AttachmentTimeline::apply (BaseSkeleton *skeleton, float time, float alpha) {

View File

@ -3,6 +3,16 @@
namespace spine {
BaseRegionAttachment::BaseRegionAttachment () :
x(0),
y(0),
scaleX(1),
scaleY(1),
rotation(0),
width(0),
height(0) {
}
void BaseRegionAttachment::updateOffset () {
float localX2 = width / 2;
float localY2 = height / 2;

View File

@ -30,7 +30,8 @@ static float toColor (const string &value, int index) {
BaseSkeletonJson::BaseSkeletonJson (BaseAttachmentLoader *attachmentLoader) :
attachmentLoader(attachmentLoader),
scale(1) {
scale(1),
flipY(false) {
}
BaseSkeletonJson::~BaseSkeletonJson () {
@ -88,12 +89,13 @@ SkeletonData* BaseSkeletonJson::readSkeletonData (const char *begin, const char
boneData->rotation = boneMap.get("rotation", 0).asDouble();
boneData->scaleX = boneMap.get("scaleX", 1).asDouble();
boneData->scaleY = boneMap.get("scaleY", 1).asDouble();
boneData->flipY = flipY;
skeletonData->bones.push_back(boneData);
}
if (root.isMember("slots")) {
Json::Value slots = root["slots"];
Json::Value slots = root["slots"];
if (!slots.isNull()) {
skeletonData->slots.reserve(slots.size());
for (int i = 0; i < slots.size(); ++i) {
Json::Value slotMap = slots[i];
@ -194,6 +196,15 @@ Animation* BaseSkeletonJson::readAnimation (const string &json, const SkeletonDa
return readAnimation(begin, end, skeletonData);
}
static void readCurve (CurveTimeline *timeline, int keyframeIndex, const Json::Value &valueMap) {
Json::Value curve = valueMap["curve"];
if (curve.isNull()) return;
if (curve.isString() && curve.asString() == "stepped")
timeline->setStepped(keyframeIndex);
else if (curve.isArray())
timeline->setCurve(keyframeIndex, curve[0u].asDouble(), curve[1u].asDouble(), curve[2u].asDouble(), curve[3u].asDouble());
}
Animation* BaseSkeletonJson::readAnimation (const char *begin, const char *end, const SkeletonData *skeletonData) const {
if (!begin) throw invalid_argument("begin cannot be null.");
if (!end) throw invalid_argument("end cannot be null.");
@ -236,8 +247,7 @@ Animation* BaseSkeletonJson::readAnimation (const char *begin, const char *end,
float time = valueMap["time"].asDouble();
timeline->setKeyframe(keyframeIndex, time, valueMap["angle"].asDouble());
// BOZO
// readCurve(timeline, keyframeIndex, valueMap);
readCurve(timeline, keyframeIndex, valueMap);
keyframeIndex++;
}
timelines.push_back(timeline);
@ -263,7 +273,7 @@ Animation* BaseSkeletonJson::readAnimation (const char *begin, const char *end,
valueMap["time"].asDouble(), //
valueMap.get("x", 0).asDouble() * timelineScale, //
valueMap.get("y", 0).asDouble() * timelineScale);
// readCurve(timeline, keyframeIndex, valueMap);
readCurve(timeline, keyframeIndex, valueMap);
keyframeIndex++;
}
timelines.push_back(timeline);
@ -275,6 +285,58 @@ Animation* BaseSkeletonJson::readAnimation (const char *begin, const char *end,
}
}
Json::Value slots = root["slots"];
if (!slots.isNull()) {
vector<string> slotNames = slots.getMemberNames();
for (int i = 0; i < slotNames.size(); i++) {
string slotName = slotNames[i];
int slotIndex = skeletonData->findSlotIndex(slotName);
if (slotIndex == -1) throw runtime_error("Slot not found: " + slotName);
Json::Value timelineMap = slots[slotName];
vector<string> timelineNames = timelineMap.getMemberNames();
for (int i = 0; i < timelineNames.size(); i++) {
string timelineName = timelineNames[i];
Json::Value values = timelineMap[timelineName];
if (timelineName == TIMELINE_COLOR) {
ColorTimeline *timeline = new ColorTimeline(values.size());
timeline->slotIndex = slotIndex;
int keyframeIndex = 0;
for (int i = 0; i < values.size(); i++) {
Json::Value valueMap = values[i];
string s = valueMap["color"].asString();
timeline->setKeyframe(keyframeIndex, valueMap["time"].asDouble(), //
toColor(s, 0), toColor(s, 1), toColor(s, 2), toColor(s, 3));
readCurve(timeline, keyframeIndex, valueMap);
keyframeIndex++;
}
timelines.push_back(timeline);
if (timeline->getDuration() > duration) duration = timeline->getDuration();
} else if (timelineName == TIMELINE_ATTACHMENT) {
AttachmentTimeline *timeline = new AttachmentTimeline(values.size());
timeline->slotIndex = slotIndex;
int keyframeIndex = 0;
for (int i = 0; i < values.size(); i++) {
Json::Value valueMap = values[i];
Json::Value name = valueMap["name"];
timeline->setKeyframe(keyframeIndex++, valueMap["time"].asDouble(), name.isNull() ? "" : name.asString());
}
timelines.push_back(timeline);
if (timeline->getDuration() > duration) duration = timeline->getDuration();
} else {
throw runtime_error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
}
}
}
}
Animation *animation = new Animation(timelines, duration);
return animation;
}

View File

@ -8,10 +8,12 @@ namespace spine {
Bone::Bone (BoneData *data) :
data(data),
parent(0),
scaleX(1),
scaleY(1) {
x(data->x),
y(data->y),
rotation(data->rotation),
scaleX(data->scaleX),
scaleY(data->scaleY) {
if (!data) throw std::invalid_argument("data cannot be null.");
setToBindPose();
}
void Bone::setToBindPose () {
@ -51,6 +53,10 @@ void Bone::updateWorldTransform (bool flipX, bool flipY) {
m10 = -m10;
m11 = -m11;
}
if (data->flipY) {
m10 = -m10;
m11 = -m11;
}
}
} /* namespace spine */

View File

@ -1,6 +1,7 @@
#include <spine/Slot.h>
#include <spine/SlotData.h>
#include <spine/BaseSkeleton.h>
#include <spine/SkeletonData.h>
namespace spine {
@ -17,6 +18,7 @@ Slot::Slot (SlotData *data, BaseSkeleton *skeleton, Bone *bone) :
if (!data) throw std::invalid_argument("data cannot be null.");
if (!skeleton) throw std::invalid_argument("skeleton cannot be null.");
if (!bone) throw std::invalid_argument("bone cannot be null.");
setToBindPose();
}
void Slot::setAttachment (Attachment *attachment) {
@ -33,8 +35,8 @@ float Slot::getAttachmentTime () const {
}
void Slot::setToBindPose () {
for (int i = 0, n = skeleton->slots.size(); i < n; i++) {
if (this == skeleton->slots[i]) {
for (int i = 0, n = skeleton->data->slots.size(); i < n; i++) {
if (data == skeleton->data->slots[i]) {
setToBindPose(i);
return;
}

View File

@ -226,12 +226,12 @@ public class SkeletonJson {
for (Entry<String, ?> entry : slotsMap.entries()) {
String slotName = entry.key;
int slotIndex = skeletonData.findSlotIndex(slotName);
OrderedMap<?, ?> propertyMap = (OrderedMap)entry.value;
OrderedMap<?, ?> timelineMap = (OrderedMap)entry.value;
for (Entry propertyEntry : propertyMap.entries()) {
Array<OrderedMap> values = (Array)propertyEntry.value;
String timelineType = (String)propertyEntry.key;
if (timelineType.equals(TIMELINE_COLOR)) {
for (Entry timelineEntry : timelineMap.entries()) {
Array<OrderedMap> values = (Array)timelineEntry.value;
String timelineName = (String)timelineEntry.key;
if (timelineName.equals(TIMELINE_COLOR)) {
ColorTimeline timeline = new ColorTimeline(values.size);
timeline.setSlotIndex(slotIndex);
@ -246,7 +246,7 @@ public class SkeletonJson {
timelines.add(timeline);
duration = Math.max(duration, timeline.getDuration());
} else if (timelineType.equals(TIMELINE_ATTACHMENT)) {
} else if (timelineName.equals(TIMELINE_ATTACHMENT)) {
AttachmentTimeline timeline = new AttachmentTimeline(values.size);
timeline.setSlotIndex(slotIndex);
@ -259,7 +259,7 @@ public class SkeletonJson {
duration = Math.max(duration, timeline.getDuration());
} else
throw new RuntimeException("Invalid timeline type for a slot: " + timelineType + " (" + slotName + ")");
throw new RuntimeException("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
}
}
}

View File

@ -26,6 +26,7 @@ public class Slot {
this.skeleton = skeleton;
this.bone = bone;
color = new Color(1, 1, 1, 1);
setToBindPose();
}
/** Copy constructor. */
@ -84,7 +85,7 @@ public class Slot {
}
public void setToBindPose () {
setToBindPose(skeleton.slots.indexOf(this, true));
setToBindPose(skeleton.data.slots.indexOf(data, true));
}
public String toString () {