From 72339ba8245d213e49f013dd2e40b0bd351b956e Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 24 Sep 2013 15:36:10 +0200 Subject: [PATCH] Bounding boxes for spine-c and spine-sfml. --- spine-c/include/spine/BoundingBoxAttachment.h | 2 +- spine-c/include/spine/RegionAttachment.h | 2 +- spine-c/include/spine/SkeletonBounds.h | 94 ++++++++ spine-c/include/spine/spine.h | 2 + spine-c/spine-c.vcxproj | 4 + spine-c/spine-c.vcxproj.filters | 12 + spine-c/src/spine/BoundingBoxAttachment.c | 2 +- spine-c/src/spine/RegionAttachment.c | 2 +- spine-c/src/spine/SkeletonBounds.c | 209 ++++++++++++++++++ spine-c/src/spine/SkeletonJson.c | 17 +- spine-sfml/example/main.cpp | 20 +- spine-sfml/src/spine/spine-sfml.cpp | 20 +- 12 files changed, 365 insertions(+), 21 deletions(-) create mode 100644 spine-c/include/spine/SkeletonBounds.h create mode 100644 spine-c/src/spine/SkeletonBounds.c diff --git a/spine-c/include/spine/BoundingBoxAttachment.h b/spine-c/include/spine/BoundingBoxAttachment.h index f13e150a9..81c19618a 100644 --- a/spine-c/include/spine/BoundingBoxAttachment.h +++ b/spine-c/include/spine/BoundingBoxAttachment.h @@ -45,8 +45,8 @@ extern "C" { typedef struct BoundingBoxAttachment BoundingBoxAttachment; struct BoundingBoxAttachment { Attachment super; - float* vertices; int verticesCount; + float* vertices; }; BoundingBoxAttachment* BoundingBoxAttachment_create (const char* name); diff --git a/spine-c/include/spine/RegionAttachment.h b/spine-c/include/spine/RegionAttachment.h index de13f1a7c..f85cc115e 100644 --- a/spine-c/include/spine/RegionAttachment.h +++ b/spine-c/include/spine/RegionAttachment.h @@ -63,7 +63,7 @@ struct RegionAttachment { RegionAttachment* RegionAttachment_create (const char* name); void RegionAttachment_setUVs (RegionAttachment* self, float u, float v, float u2, float v2, int/*bool*/rotate); void RegionAttachment_updateOffset (RegionAttachment* self); -void RegionAttachment_computeVertices (RegionAttachment* self, float x, float y, Bone* bone, float* vertices); +void RegionAttachment_computeWorldVertices (RegionAttachment* self, float x, float y, Bone* bone, float* vertices); #ifdef __cplusplus } diff --git a/spine-c/include/spine/SkeletonBounds.h b/spine-c/include/spine/SkeletonBounds.h new file mode 100644 index 000000000..e0816c0a6 --- /dev/null +++ b/spine-c/include/spine/SkeletonBounds.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.0 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Single User License or Spine Professional License must be + * purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef SPINE_SKELETONBOUNDS_H_ +#define SPINE_SKELETONBOUNDS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + float* const vertices; + int count; + int capacity; +} Polygon; + +Polygon* Polygon_create (int capacity); +void Polygon_dispose (Polygon* self); + +int/*bool*/Polygon_containsPoint (Polygon* polygon, float x, float y); +int/*bool*/Polygon_intersectsSegment (Polygon* polygon, float x1, float y1, float x2, float y2); + +/**/ + +typedef struct { + int count; + BoundingBoxAttachment** boundingBoxes; + Polygon** polygons; + + float minX, minY, maxX, maxY; +} SkeletonBounds; + +SkeletonBounds* SkeletonBounds_create (); +void SkeletonBounds_dispose (SkeletonBounds* self); +void SkeletonBounds_update (SkeletonBounds* self, Skeleton* skeleton, int/*bool*/updateAabb); + +/** Returns true if the axis aligned bounding box contains the point. */ +int/*bool*/SkeletonBounds_aabbContainsPoint (SkeletonBounds* self, float x, float y); + +/** Returns true if the axis aligned bounding box intersects the line segment. */ +int/*bool*/SkeletonBounds_aabbIntersectsSegment (SkeletonBounds* self, float x1, float y1, float x2, float y2); + +/** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ +int/*bool*/SkeletonBounds_aabbIntersectsSkeleton (SkeletonBounds* self, SkeletonBounds* bounds); + +/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more + * efficient to only call this method if SkeletonBounds_aabbContainsPoint returns true. */ +BoundingBoxAttachment* SkeletonBounds_containsPoint (SkeletonBounds* self, float x, float y); + +/** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually + * more efficient to only call this method if SkeletonBounds_aabbIntersectsSegment returns true. */ +BoundingBoxAttachment* SkeletonBounds_intersectsSegment (SkeletonBounds* self, float x1, float y1, float x2, float y2); + +/** Returns the polygon for the specified bounding box, or null. */ +Polygon* SkeletonBounds_getPolygon (SkeletonBounds* self, BoundingBoxAttachment* boundingBox); + +#ifdef __cplusplus +} +#endif + +#endif /* SPINE_SKELETONBOUNDS_H_ */ diff --git a/spine-c/include/spine/spine.h b/spine-c/include/spine/spine.h index 7119f1e61..72aa12fea 100644 --- a/spine-c/include/spine/spine.h +++ b/spine-c/include/spine/spine.h @@ -44,7 +44,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/spine-c/spine-c.vcxproj b/spine-c/spine-c.vcxproj index 197cf32d5..a65aa687d 100644 --- a/spine-c/spine-c.vcxproj +++ b/spine-c/spine-c.vcxproj @@ -80,9 +80,11 @@ + + @@ -101,10 +103,12 @@ + + diff --git a/spine-c/spine-c.vcxproj.filters b/spine-c/spine-c.vcxproj.filters index 39653ecee..1ee8b0a6f 100644 --- a/spine-c/spine-c.vcxproj.filters +++ b/spine-c/spine-c.vcxproj.filters @@ -72,6 +72,12 @@ Header Files + + Header Files + + + Header Files + @@ -128,5 +134,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/spine-c/src/spine/BoundingBoxAttachment.c b/spine-c/src/spine/BoundingBoxAttachment.c index 3f4f8252b..07cc1aeaf 100644 --- a/spine-c/src/spine/BoundingBoxAttachment.c +++ b/spine-c/src/spine/BoundingBoxAttachment.c @@ -41,7 +41,7 @@ BoundingBoxAttachment* BoundingBoxAttachment_create (const char* name) { return self; } -void BoundingBoxAttachment_computeVertices (BoundingBoxAttachment* self, float x, float y, Bone* bone, float* worldVertices) { +void BoundingBoxAttachment_computeWorldVertices (BoundingBoxAttachment* self, float x, float y, Bone* bone, float* worldVertices) { int i; float px, py; float* vertices = self->vertices; diff --git a/spine-c/src/spine/RegionAttachment.c b/spine-c/src/spine/RegionAttachment.c index 851ec2153..1d75827d0 100644 --- a/spine-c/src/spine/RegionAttachment.c +++ b/spine-c/src/spine/RegionAttachment.c @@ -98,7 +98,7 @@ void RegionAttachment_updateOffset (RegionAttachment* self) { self->offset[VERTEX_Y4] = localYCos + localX2Sin; } -void RegionAttachment_computeVertices (RegionAttachment* self, float x, float y, Bone* bone, float* vertices) { +void RegionAttachment_computeWorldVertices (RegionAttachment* self, float x, float y, Bone* bone, float* vertices) { float* offset = self->offset; x += bone->worldX; y += bone->worldY; diff --git a/spine-c/src/spine/SkeletonBounds.c b/spine-c/src/spine/SkeletonBounds.c new file mode 100644 index 000000000..b12d7e7e4 --- /dev/null +++ b/spine-c/src/spine/SkeletonBounds.c @@ -0,0 +1,209 @@ +/****************************************************************************** + * Spine Runtime Software License - Version 1.0 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms in whole or in part, with + * or without modification, are permitted provided that the following conditions + * are met: + * + * 1. A Spine Single User License or Spine Professional License must be + * purchased from Esoteric Software and the license must remain valid: + * http://esotericsoftware.com/ + * 2. Redistributions of source code must retain this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer. + * 3. Redistributions in binary form must reproduce this license, which is the + * above copyright notice, this declaration of conditions and the following + * disclaimer, in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include +#include +#include +#include + +Polygon* Polygon_create (int capacity) { + Polygon* self = NEW(Polygon); + self->capacity = capacity; + CONST_CAST(float*, self->vertices) = MALLOC(float, capacity); + return self; +} + +void Polygon_dispose (Polygon* self) { + FREE(self->vertices); + FREE(self); +} + +int/*bool*/Polygon_containsPoint (Polygon* self, float x, float y) { + int prevIndex = self->count - 2; + int inside = 0; + int i = 0; + for (; i < self->count; i += 2) { + float vertexY = self->vertices[i + 1]; + float prevY = self->vertices[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + float vertexX = self->vertices[i]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (self->vertices[prevIndex] - vertexX) < x) inside = !inside; + } + prevIndex = i; + } + return inside; +} + +int/*bool*/Polygon_intersectsSegment (Polygon* self, float x1, float y1, float x2, float y2) { + float width12 = x1 - x2, height12 = y1 - y2; + float det1 = x1 * y2 - y1 * x2; + float x3 = self->vertices[self->count - 2], y3 = self->vertices[self->count - 1]; + int i = 0; + for (; i < self->count; i += 2) { + float x4 = self->vertices[i], y4 = self->vertices[i + 1]; + float det2 = x3 * y4 - y3 * x4; + float width34 = x3 - x4, height34 = y3 - y4; + float det3 = width12 * height34 - height12 * width34; + float x = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { + float y = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return 1; + } + x3 = x4; + y3 = y4; + } + return 0; +} + +/**/ + +typedef struct { + SkeletonBounds super; + int capacity; +} _Internal; + +SkeletonBounds* SkeletonBounds_create () { + return SUPER(NEW(_Internal)); +} + +void SkeletonBounds_dispose (SkeletonBounds* self) { + int i = 0; + for (; i < SUB_CAST(_Internal, self)->capacity; ++i) + Polygon_dispose(self->polygons[i]); + FREE(self->polygons); + FREE(self->boundingBoxes); + FREE(self); +} + +void SkeletonBounds_update (SkeletonBounds* self, Skeleton* skeleton, int/*bool*/updateAabb) { + int i; + + _Internal* internal = SUB_CAST(_Internal, self); + if (internal->capacity < skeleton->slotCount) { + Polygon** newPolygons; + + FREE(self->boundingBoxes); + self->boundingBoxes = MALLOC(BoundingBoxAttachment*, skeleton->slotCount); + + newPolygons = CALLOC(Polygon*, skeleton->slotCount); + memcpy(newPolygons, self->polygons, internal->capacity); + FREE(self->polygons); + self->polygons = newPolygons; + + internal->capacity = skeleton->slotCount; + } + + self->minX = (float)INT_MAX; + self->minY = (float)INT_MIN; + self->maxX = (float)INT_MAX; + self->maxY = (float)INT_MIN; + + self->count = 0; + for (i = 0; i < skeleton->slotCount; ++i) { + Polygon* polygon; + BoundingBoxAttachment* boundingBox; + + Slot* slot = skeleton->slots[i]; + Attachment* attachment = slot->attachment; + if (!attachment || attachment->type != ATTACHMENT_BOUNDING_BOX) continue; + boundingBox = (BoundingBoxAttachment*)attachment; + self->boundingBoxes[self->count] = boundingBox; + + polygon = self->polygons[self->count]; + if (!polygon || polygon->capacity < boundingBox->verticesCount) { + if (polygon) Polygon_dispose(polygon); + self->polygons[self->count] = polygon = Polygon_create(boundingBox->verticesCount); + } + polygon->count = boundingBox->verticesCount; + BoundingBoxAttachment_computeWorldVertices(boundingBox, skeleton->x, skeleton->y, slot->bone, polygon->vertices); + + if (updateAabb) { + int ii = 0; + for (; ii < polygon->count; ii += 2) { + float x = polygon->vertices[ii]; + float y = polygon->vertices[ii + 1]; + if (x < self->minX) self->minX = x; + if (y < self->minY) self->minY = y; + if (x > self->maxX) self->maxX = x; + if (y > self->maxY) self->maxY = y; + } + } + + ++self->count; + } +} + +int/*bool*/SkeletonBounds_aabbContainsPoint (SkeletonBounds* self, float x, float y) { + return x >= self->minX && x <= self->maxX && y >= self->minY && y <= self->maxY; +} + +int/*bool*/SkeletonBounds_aabbIntersectsSegment (SkeletonBounds* self, float x1, float y1, float x2, float y2) { + float m, x, y; + if ((x1 <= self->minX && x2 <= self->minX) || (y1 <= self->minY && y2 <= self->minY) || (x1 >= self->maxX && x2 >= self->maxX) + || (y1 >= self->maxY && y2 >= self->maxY)) return 0; + m = (y2 - y1) / (x2 - x1); + y = m * (self->minX - x1) + y1; + if (y > self->minY && y < self->maxY) return 1; + y = m * (self->maxX - x1) + y1; + if (y > self->minY && y < self->maxY) return 1; + x = (self->minY - y1) / m + x1; + if (x > self->minX && x < self->maxX) return 1; + x = (self->maxY - y1) / m + x1; + if (x > self->minX && x < self->maxX) return 1; + return 0; +} + +int/*bool*/SkeletonBounds_aabbIntersectsSkeleton (SkeletonBounds* self, SkeletonBounds* bounds) { + return self->minX < bounds->maxX && self->maxX > bounds->minX && self->minY < bounds->maxY && self->maxY > bounds->minY; +} + +BoundingBoxAttachment* SkeletonBounds_containsPoint (SkeletonBounds* self, float x, float y) { + int i = 0; + for (; i < self->count; ++i) + if (Polygon_containsPoint(self->polygons[i], x, y)) return self->boundingBoxes[i]; + return 0; +} + +BoundingBoxAttachment* SkeletonBounds_intersectsSegment (SkeletonBounds* self, float x1, float y1, float x2, float y2) { + int i = 0; + for (; i < self->count; ++i) + if (Polygon_intersectsSegment(self->polygons[i], x1, y1, x2, y2)) return self->boundingBoxes[i]; + return 0; +} + +Polygon* SkeletonBounds_getPolygon (SkeletonBounds* self, BoundingBoxAttachment* boundingBox) { + int i = 0; + for (; i < self->count; ++i) + if (self->boundingBoxes[i] == boundingBox) return self->polygons[i]; + return 0; +} diff --git a/spine-c/src/spine/SkeletonJson.c b/spine-c/src/spine/SkeletonJson.c index dd958d757..1f800ac30 100644 --- a/spine-c/src/spine/SkeletonJson.c +++ b/spine-c/src/spine/SkeletonJson.c @@ -411,7 +411,9 @@ SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* jso continue; } - if (attachment->type == ATTACHMENT_REGION || attachment->type == ATTACHMENT_REGION_SEQUENCE) { + switch (attachment->type) { + case ATTACHMENT_REGION: + case ATTACHMENT_REGION_SEQUENCE: { RegionAttachment* regionAttachment = (RegionAttachment*)attachment; regionAttachment->x = Json_getFloat(attachmentMap, "x", 0) * self->scale; regionAttachment->y = Json_getFloat(attachmentMap, "y", 0) * self->scale; @@ -421,6 +423,19 @@ SkeletonData* SkeletonJson_readSkeletonData (SkeletonJson* self, const char* jso regionAttachment->width = Json_getFloat(attachmentMap, "width", 32) * self->scale; regionAttachment->height = Json_getFloat(attachmentMap, "height", 32) * self->scale; RegionAttachment_updateOffset(regionAttachment); + break; + } + case ATTACHMENT_BOUNDING_BOX: { + BoundingBoxAttachment* box = (BoundingBoxAttachment*)attachment; + Json* verticesArray = Json_getItem(attachmentMap, "vertices"); + Json* vertex; + int i = 0; + box->verticesCount = verticesArray->size; + box->vertices = MALLOC(float, verticesArray->size); + for (vertex = verticesArray->child; vertex; vertex = vertex->next, ++i) + box->vertices[i] = vertex->valueFloat; + break; + } } Skin_addAttachment(skin, slotIndex, skinAttachmentName, attachment); diff --git a/spine-sfml/example/main.cpp b/spine-sfml/example/main.cpp index 89772e601..4510ed7f8 100644 --- a/spine-sfml/example/main.cpp +++ b/spine-sfml/example/main.cpp @@ -35,6 +35,7 @@ #include #include #include +#include using namespace std; using namespace spine; @@ -50,6 +51,7 @@ void spineboy () { exit(0); } SkeletonJson_dispose(json); + SkeletonBounds* bounds = SkeletonBounds_create(); // Configure mixing. AnimationStateData* stateData = AnimationStateData_create(skeletonData); @@ -68,6 +70,8 @@ void spineboy () { skeleton->root->y = 420; Skeleton_updateWorldTransform(skeleton); + Slot* headSlot = Skeleton_findSlot(skeleton, "head"); + if (true) { AnimationState_setAnimationByName(drawable->state, "drawOrder", true); } else { @@ -87,11 +91,15 @@ void spineboy () { float delta = deltaClock.getElapsedTime().asSeconds(); deltaClock.restart(); - /*if (drawable->state->loop) { - if (drawable->state->time > 2) AnimationState_setAnimationByName(drawable->state, "jump", false); - } else { - if (drawable->state->time > 1) AnimationState_setAnimationByName(drawable->state, "walk", true); - }*/ + SkeletonBounds_update(bounds, skeleton, true); + sf::Vector2i position = sf::Mouse::getPosition(window); + if (SkeletonBounds_containsPoint(bounds, position.x, position.y)) { + headSlot->g = 0; + headSlot->b = 0; + } else { + headSlot->g = 1; + headSlot->b = 1; + } drawable->update(delta); @@ -105,7 +113,7 @@ void spineboy () { } void goblins () { - // Load atlas, skeleton, and animations. +// Load atlas, skeleton, and animations. Atlas* atlas = Atlas_readAtlasFile("../data/goblins.atlas"); SkeletonJson* json = SkeletonJson_create(atlas); json->scale = 2; diff --git a/spine-sfml/src/spine/spine-sfml.cpp b/spine-sfml/src/spine/spine-sfml.cpp index 30338b01f..4a22cc1cd 100644 --- a/spine-sfml/src/spine/spine-sfml.cpp +++ b/spine-sfml/src/spine/spine-sfml.cpp @@ -89,7 +89,7 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const { vertexArray->clear(); states.blendMode = BlendAlpha; - float vertexPositions[8]; + float worldVertices[8]; for (int i = 0; i < skeleton->slotCount; ++i) { Slot* slot = skeleton->drawOrder[i]; Attachment* attachment = slot->attachment; @@ -103,7 +103,7 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const { states.blendMode = blend; } - RegionAttachment_computeVertices(regionAttachment, slot->skeleton->x, slot->skeleton->y, slot->bone, vertexPositions); + RegionAttachment_computeWorldVertices(regionAttachment, slot->skeleton->x, slot->skeleton->y, slot->bone, worldVertices); Uint8 r = skeleton->r * slot->r * 255; Uint8 g = skeleton->g * slot->g * 255; @@ -128,14 +128,14 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const { vertices[3].color.b = b; vertices[3].color.a = a; - vertices[0].position.x = vertexPositions[VERTEX_X1]; - vertices[0].position.y = vertexPositions[VERTEX_Y1]; - vertices[1].position.x = vertexPositions[VERTEX_X2]; - vertices[1].position.y = vertexPositions[VERTEX_Y2]; - vertices[2].position.x = vertexPositions[VERTEX_X3]; - vertices[2].position.y = vertexPositions[VERTEX_Y3]; - vertices[3].position.x = vertexPositions[VERTEX_X4]; - vertices[3].position.y = vertexPositions[VERTEX_Y4]; + vertices[0].position.x = worldVertices[VERTEX_X1]; + vertices[0].position.y = worldVertices[VERTEX_Y1]; + vertices[1].position.x = worldVertices[VERTEX_X2]; + vertices[1].position.y = worldVertices[VERTEX_Y2]; + vertices[2].position.x = worldVertices[VERTEX_X3]; + vertices[2].position.y = worldVertices[VERTEX_Y3]; + vertices[3].position.x = worldVertices[VERTEX_X4]; + vertices[3].position.y = worldVertices[VERTEX_Y4]; // SMFL doesn't handle batching for us, so we'll just force a single texture per skeleton. states.texture = (Texture*)((AtlasRegion*)regionAttachment->rendererObject)->page->rendererObject;