Bounding boxes for spine-c and spine-sfml.

This commit is contained in:
NathanSweet 2013-09-24 15:36:10 +02:00
parent 76778031ed
commit 72339ba824
12 changed files with 365 additions and 21 deletions

View File

@ -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);

View File

@ -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
}

View File

@ -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 <spine/BoundingBoxAttachment.h>
#include <spine/Skeleton.h>
#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_ */

View File

@ -44,7 +44,9 @@
#include <spine/Bone.h>
#include <spine/BoneData.h>
#include <spine/RegionAttachment.h>
#include <spine/BoundingBoxAttachment.h>
#include <spine/Skeleton.h>
#include <spine/SkeletonBounds.h>
#include <spine/SkeletonData.h>
#include <spine/SkeletonJson.h>
#include <spine/Skin.h>

View File

@ -80,9 +80,11 @@
<ClInclude Include="include\spine\AttachmentLoader.h" />
<ClInclude Include="include\spine\Bone.h" />
<ClInclude Include="include\spine\BoneData.h" />
<ClInclude Include="include\spine\BoundingBoxAttachment.h" />
<ClInclude Include="include\spine\extension.h" />
<ClInclude Include="include\spine\RegionAttachment.h" />
<ClInclude Include="include\spine\Skeleton.h" />
<ClInclude Include="include\spine\SkeletonBounds.h" />
<ClInclude Include="include\spine\SkeletonData.h" />
<ClInclude Include="include\spine\SkeletonJson.h" />
<ClInclude Include="include\spine\Skin.h" />
@ -101,10 +103,12 @@
<ClCompile Include="src\spine\AttachmentLoader.c" />
<ClCompile Include="src\spine\Bone.c" />
<ClCompile Include="src\spine\BoneData.c" />
<ClCompile Include="src\spine\BoundingBoxAttachment.c" />
<ClCompile Include="src\spine\extension.c" />
<ClCompile Include="src\spine\Json.c" />
<ClCompile Include="src\spine\RegionAttachment.c" />
<ClCompile Include="src\spine\Skeleton.c" />
<ClCompile Include="src\spine\SkeletonBounds.c" />
<ClCompile Include="src\spine\SkeletonData.c" />
<ClCompile Include="src\spine\SkeletonJson.c" />
<ClCompile Include="src\spine\Skin.c" />

View File

@ -72,6 +72,12 @@
<ClInclude Include="src\spine\Json.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\spine\BoundingBoxAttachment.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\spine\SkeletonBounds.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\spine\Animation.c">
@ -128,5 +134,11 @@
<ClCompile Include="src\spine\SlotData.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\spine\BoundingBoxAttachment.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\spine\SkeletonBounds.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -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;

View File

@ -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;

View File

@ -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 <spine/SkeletonBounds.h>
#include <math.h>
#include <limits.h>
#include <spine/extension.h>
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;
}

View File

@ -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);

View File

@ -35,6 +35,7 @@
#include <string.h>
#include <spine/spine-sfml.h>
#include <SFML/Graphics.hpp>
#include <SFML/Window/Mouse.hpp>
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;

View File

@ -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;