mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-12 10:08:44 +08:00
Yay, now the C++ Runtime passes all the same tests as the C Runtime!
This commit is contained in:
parent
c1995586aa
commit
e24b061c61
@ -281,6 +281,7 @@
|
||||
BB6017F01FD92A5B009BD546 /* SimpleTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleTest.h; sourceTree = "<group>"; };
|
||||
BB6017FC1FD92AF3009BD546 /* SpineEventMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpineEventMonitor.h; sourceTree = "<group>"; };
|
||||
BB6017FD1FD92AF3009BD546 /* SpineEventMonitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpineEventMonitor.cpp; sourceTree = "<group>"; };
|
||||
BBFB507F1FDAF6CD005B22B6 /* MemoryTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MemoryTest.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -326,6 +327,7 @@
|
||||
children = (
|
||||
BB6017121FD9289A009BD546 /* main.cpp */,
|
||||
BB6017F01FD92A5B009BD546 /* SimpleTest.h */,
|
||||
BBFB507F1FDAF6CD005B22B6 /* MemoryTest.h */,
|
||||
);
|
||||
path = spine_unit_test;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@ -0,0 +1,444 @@
|
||||
//
|
||||
// MemoryTest.h
|
||||
// spine_unit_test
|
||||
//
|
||||
// Created by Stephen Gowen on 12/8/17.
|
||||
// Copyright © 2017 Noctis Games. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MemoryTest_h
|
||||
#define MemoryTest_h
|
||||
|
||||
#include "SpineEventMonitor.h"
|
||||
|
||||
#include <spine/SkeletonJson.h>
|
||||
#include <spine/SkeletonData.h>
|
||||
#include <spine/Atlas.h>
|
||||
#include <spine/AnimationStateData.h>
|
||||
#include <spine/Skeleton.h>
|
||||
#include <spine/AnimationState.h>
|
||||
#include <spine/Animation.h>
|
||||
|
||||
#include <vector>
|
||||
#include <spine/Extension.h>
|
||||
#include <spine/TextureLoader.h>
|
||||
#include <spine/Vector.h>
|
||||
|
||||
#include <spine/CurveTimeline.h>
|
||||
#include <spine/VertexAttachment.h>
|
||||
#include <spine/Json.h>
|
||||
|
||||
#include <spine/AttachmentLoader.h>
|
||||
#include <spine/AtlasAttachmentLoader.h>
|
||||
#include <spine/LinkedMesh.h>
|
||||
#include <spine/Triangulator.h>
|
||||
#include <spine/SkeletonClipping.h>
|
||||
#include <spine/BoneData.h>
|
||||
#include <spine/Bone.h>
|
||||
#include <spine/SlotData.h>
|
||||
#include <spine/Slot.h>
|
||||
#include <spine/ClippingAttachment.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "KMemory.h" // last include
|
||||
|
||||
#define SPINEBOY_JSON "testdata/spineboy/spineboy-ess.json"
|
||||
#define SPINEBOY_ATLAS "testdata/spineboy/spineboy.atlas"
|
||||
|
||||
#define MAX_RUN_TIME 6000 // equal to about 100 seconds of execution
|
||||
|
||||
namespace Spine
|
||||
{
|
||||
class MemoryTest
|
||||
{
|
||||
public:
|
||||
class MyTextureLoader : public TextureLoader
|
||||
{
|
||||
virtual void load(AtlasPage& page, std::string path)
|
||||
{
|
||||
page.rendererObject = NULL;
|
||||
page.width = 2048;
|
||||
page.height = 2048;
|
||||
}
|
||||
|
||||
virtual void unload(void* texture)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Helper methods
|
||||
static SkeletonData* readSkeletonJsonData(const char* filename, Atlas* atlas)
|
||||
{
|
||||
Vector<Atlas*> atlasArray;
|
||||
atlasArray.push_back(atlas);
|
||||
|
||||
SkeletonJson* skeletonJson = NEW(SkeletonJson);
|
||||
new (skeletonJson) SkeletonJson(atlasArray);
|
||||
assert(skeletonJson != 0);
|
||||
|
||||
SkeletonData* skeletonData = skeletonJson->readSkeletonDataFile(filename);
|
||||
assert(skeletonData != 0);
|
||||
|
||||
DESTROY(SkeletonJson, skeletonJson);
|
||||
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
static void loadSpineboyExample(Atlas* &atlas, SkeletonData* &skeletonData, AnimationStateData* &stateData, Skeleton* &skeleton, AnimationState* &state)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Global Animation Information
|
||||
static MyTextureLoader myTextureLoader;
|
||||
atlas = NEW(Atlas);
|
||||
new (atlas) Atlas(SPINEBOY_ATLAS, myTextureLoader);
|
||||
assert(atlas != 0);
|
||||
|
||||
skeletonData = readSkeletonJsonData(SPINEBOY_JSON, atlas);
|
||||
assert(skeletonData != 0);
|
||||
|
||||
stateData = NEW(AnimationStateData);
|
||||
new (stateData) AnimationStateData(*skeletonData);
|
||||
assert(stateData != 0);
|
||||
stateData->setDefaultMix(0.2f); // force mixing
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Animation Instance
|
||||
skeleton = NEW(Skeleton);
|
||||
new (skeleton) Skeleton(*skeletonData);
|
||||
assert(skeleton != 0);
|
||||
|
||||
state = NEW(AnimationState);
|
||||
new (state) AnimationState(*stateData);
|
||||
assert(state != 0);
|
||||
}
|
||||
|
||||
static void disposeAll(Skeleton* skeleton, AnimationState* state, AnimationStateData* stateData, SkeletonData* skeletonData, Atlas* atlas)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Dispose Instance
|
||||
DESTROY(Skeleton, skeleton);
|
||||
DESTROY(AnimationState, state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Dispose Global
|
||||
DESTROY(AnimationStateData, stateData);
|
||||
DESTROY(SkeletonData, skeletonData);
|
||||
DESTROY(Atlas, atlas);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Reproduce Memory leak as described in Issue #776
|
||||
// https://github.com/EsotericSoftware/spine-runtimes/issues/776
|
||||
static void reproduceIssue_776()
|
||||
{
|
||||
Atlas* atlas = NULL;
|
||||
SkeletonData* skeletonData = NULL;
|
||||
AnimationStateData* stateData = NULL;
|
||||
Skeleton* skeleton = NULL;
|
||||
AnimationState* state = NULL;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Initialize Animations
|
||||
loadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Run animation
|
||||
skeleton->setToSetupPose();
|
||||
InterruptMonitor eventMonitor(state);
|
||||
|
||||
// Interrupt the animation on this specific sequence of spEventType(s)
|
||||
eventMonitor
|
||||
.AddInterruptEvent(EventType_Interrupt, "jump")
|
||||
.AddInterruptEvent(EventType_Start);
|
||||
|
||||
state->setAnimation(0, "walk", true);
|
||||
state->addAnimation(0, "jump", false, 0.0f);
|
||||
state->addAnimation(0, "run", true, 0.0f);
|
||||
state->addAnimation(0, "jump", false, 3.0f);
|
||||
state->addAnimation(0, "walk", true, 0.0f);
|
||||
state->addAnimation(0, "idle", false, 1.0f);
|
||||
|
||||
for (int i = 0; i < MAX_RUN_TIME && eventMonitor.isAnimationPlaying(); ++i)
|
||||
{
|
||||
const float timeSlice = 1.0f / 60.0f;
|
||||
skeleton->update(timeSlice);
|
||||
state->update(timeSlice);
|
||||
state->apply(*skeleton);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Cleanup Animations
|
||||
disposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
}
|
||||
|
||||
static void reproduceIssue_777()
|
||||
{
|
||||
Atlas* atlas = NULL;
|
||||
SkeletonData* skeletonData = NULL;
|
||||
AnimationStateData* stateData = NULL;
|
||||
Skeleton* skeleton = NULL;
|
||||
AnimationState* state = NULL;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Initialize Animations
|
||||
loadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Run animation
|
||||
skeleton->setToSetupPose();
|
||||
SpineEventMonitor eventMonitor(state);
|
||||
|
||||
// Set Animation and Play for 5 frames
|
||||
state->setAnimation(0, "walk", true);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
const float timeSlice = 1.0f / 60.0f;
|
||||
skeleton->update(timeSlice);
|
||||
state->update(timeSlice);
|
||||
state->apply(*skeleton);
|
||||
}
|
||||
|
||||
// Change animation twice in a row
|
||||
state->setAnimation(0, "walk", false);
|
||||
state->setAnimation(0, "run", false);
|
||||
|
||||
// run normal update
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
const float timeSlice = 1.0f / 60.0f;
|
||||
skeleton->update(timeSlice);
|
||||
state->update(timeSlice);
|
||||
state->apply(*skeleton);
|
||||
}
|
||||
|
||||
// Now we'd lose mixingFrom (the first "walk" entry we set above) and should leak
|
||||
state->setAnimation(0, "run", false);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Cleanup Animations
|
||||
disposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
}
|
||||
|
||||
static void spineAnimStateHandler(AnimationState* state, EventType type, TrackEntry* entry, Event* event)
|
||||
{
|
||||
if (type == EventType_Complete)
|
||||
{
|
||||
state->setAnimation(0, "walk", false);
|
||||
state->update(0);
|
||||
state->apply(*skeleton);
|
||||
}
|
||||
}
|
||||
|
||||
static void reproduceIssue_Loop()
|
||||
{
|
||||
Atlas* atlas = NULL;
|
||||
SkeletonData* skeletonData = NULL;
|
||||
AnimationStateData* stateData = NULL;
|
||||
AnimationState* state = NULL;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Initialize Animations
|
||||
loadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (state)
|
||||
{
|
||||
state->setOnAnimationEventFunc(spineAnimStateHandler);
|
||||
}
|
||||
|
||||
state->setAnimation(0, "walk", false);
|
||||
|
||||
// run normal update
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
const float timeSlice = 1.0f / 60.0f;
|
||||
skeleton->update(timeSlice);
|
||||
state->update(timeSlice);
|
||||
state->apply(*skeleton);
|
||||
}
|
||||
|
||||
disposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
}
|
||||
|
||||
static void triangulator()
|
||||
{
|
||||
Triangulator* triangulator = NEW(Triangulator);
|
||||
new (triangulator) Triangulator();
|
||||
|
||||
Vector<float> polygon;
|
||||
polygon.reserve(16);
|
||||
polygon.push_back(0);
|
||||
polygon.push_back(0);
|
||||
polygon.push_back(100);
|
||||
polygon.push_back(0);
|
||||
polygon.push_back(100);
|
||||
polygon.push_back(100);
|
||||
polygon.push_back(0);
|
||||
polygon.push_back(100);
|
||||
|
||||
Vector<int> triangles = triangulator->triangulate(polygon);
|
||||
assert(triangles.size() == 6);
|
||||
assert(triangles[0] == 3);
|
||||
assert(triangles[1] == 0);
|
||||
assert(triangles[2] == 1);
|
||||
assert(triangles[3] == 3);
|
||||
assert(triangles[4] == 1);
|
||||
assert(triangles[5] == 2);
|
||||
|
||||
Vector< Vector<float> *> polys = triangulator->decompose(polygon, triangles);
|
||||
assert(polys.size() == 1);
|
||||
assert(polys[0]->size() == 8);
|
||||
|
||||
assert(polys[0]->operator[](0) == 0);
|
||||
assert(polys[0]->operator[](1) == 100);
|
||||
assert(polys[0]->operator[](2) == 0);
|
||||
assert(polys[0]->operator[](3) == 0);
|
||||
assert(polys[0]->operator[](4) == 100);
|
||||
assert(polys[0]->operator[](5) == 0);
|
||||
assert(polys[0]->operator[](6) == 100);
|
||||
assert(polys[0]->operator[](7) == 100);
|
||||
|
||||
DESTROY(Triangulator, triangulator);
|
||||
}
|
||||
|
||||
static void skeletonClipper()
|
||||
{
|
||||
Atlas* atlas = NULL;
|
||||
SkeletonData* skeletonData = NULL;
|
||||
AnimationStateData* stateData = NULL;
|
||||
Skeleton* skeleton = NULL;
|
||||
AnimationState* state = NULL;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Initialize Animations
|
||||
loadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
|
||||
SkeletonClipping* clipping = NEW(SkeletonClipping);
|
||||
new (clipping) SkeletonClipping();
|
||||
|
||||
BoneData* boneData = NEW(BoneData);
|
||||
new (boneData) BoneData(0, "bone", 0);
|
||||
|
||||
Bone* bone = NEW(Bone);
|
||||
new(bone) Bone(*boneData, *skeleton, NULL);
|
||||
|
||||
bone->setA(1);
|
||||
bone->setB(0);
|
||||
bone->setC(0);
|
||||
bone->setD(1);
|
||||
bone->setWorldX(0);
|
||||
bone->setWorldY(0);
|
||||
|
||||
SlotData* slotData = NEW(SlotData);
|
||||
new (slotData) SlotData(0, "slot", *boneData);
|
||||
|
||||
Slot* slot = NEW(Slot);
|
||||
new(slot) Slot(*slotData, *bone);
|
||||
|
||||
ClippingAttachment* clip = NEW(ClippingAttachment);
|
||||
new(clip) ClippingAttachment("clipping");
|
||||
|
||||
clip->setEndSlot(slotData);
|
||||
clip->setWorldVerticesLength(4 * 2);
|
||||
|
||||
Vector<float> clipVertices;
|
||||
clipVertices.reserve(8);
|
||||
clipVertices.setSize(8);
|
||||
|
||||
clip->setVertices(clipVertices);
|
||||
clip->getVertices()[0] = 0;
|
||||
clip->getVertices()[1] = 50;
|
||||
clip->getVertices()[2] = 100;
|
||||
clip->getVertices()[3] = 50;
|
||||
clip->getVertices()[4] = 100;
|
||||
clip->getVertices()[5] = 70;
|
||||
clip->getVertices()[6] = 0;
|
||||
clip->getVertices()[7] = 70;
|
||||
|
||||
clipping->clipStart(*slot, clip);
|
||||
|
||||
Vector<float> vertices;
|
||||
vertices.reserve(16);
|
||||
vertices.push_back(0);
|
||||
vertices.push_back(0);
|
||||
vertices.push_back(100);
|
||||
vertices.push_back(0);
|
||||
vertices.push_back(50);
|
||||
vertices.push_back(150);
|
||||
|
||||
Vector<float> uvs;
|
||||
uvs.reserve(16);
|
||||
uvs.push_back(0);
|
||||
uvs.push_back(0);
|
||||
uvs.push_back(1);
|
||||
uvs.push_back(0);
|
||||
uvs.push_back(0.5f);
|
||||
uvs.push_back(1);
|
||||
|
||||
Vector<int> indices;
|
||||
indices.reserve(16);
|
||||
indices.push_back(0);
|
||||
indices.push_back(1);
|
||||
indices.push_back(2);
|
||||
|
||||
clipping->clipTriangles(vertices, static_cast<int>(vertices.size()), indices, static_cast<int>(indices.size()), uvs);
|
||||
|
||||
float expectedVertices[8] = { 83.333328, 50.000000, 76.666664, 70.000000, 23.333334, 70.000000, 16.666672, 50.000000 };
|
||||
assert(clipping->getClippedVertices().size() == 8);
|
||||
for (int i = 0; i < clipping->getClippedVertices().size(); i++)
|
||||
{
|
||||
assert(abs(clipping->getClippedVertices()[i] - expectedVertices[i]) < 0.001);
|
||||
}
|
||||
|
||||
float expectedUVs[8] = { 0.833333f, 0.333333, 0.766667, 0.466667, 0.233333, 0.466667, 0.166667, 0.333333 };
|
||||
assert(clipping->getClippedUVs().size() == 8);
|
||||
for (int i = 0; i < clipping->getClippedUVs().size(); i++)
|
||||
{
|
||||
assert(abs(clipping->getClippedUVs()[i] - expectedUVs[i]) < 0.001);
|
||||
}
|
||||
|
||||
short expectedIndices[6] = { 0, 1, 2, 0, 2, 3 };
|
||||
assert(clipping->getClippedTriangles().size() == 6);
|
||||
for (int i = 0; i < clipping->getClippedTriangles().size(); i++)
|
||||
{
|
||||
assert(clipping->getClippedTriangles()[i] == expectedIndices[i]);
|
||||
}
|
||||
|
||||
DESTROY(SlotData, slotData);
|
||||
DESTROY(Slot, slot);
|
||||
DESTROY(BoneData, boneData);
|
||||
DESTROY(Bone, bone);
|
||||
DESTROY(ClippingAttachment, clip);
|
||||
DESTROY(SkeletonClipping, clipping);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Cleanup Animations
|
||||
disposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
}
|
||||
|
||||
static void test()
|
||||
{
|
||||
reproduceIssue_776();
|
||||
reproduceIssue_777();
|
||||
reproduceIssue_Loop();
|
||||
triangulator();
|
||||
skeletonClipper();
|
||||
}
|
||||
|
||||
private:
|
||||
static Skeleton* skeleton;
|
||||
|
||||
// ctor, copy ctor, and assignment should be private in a Singleton
|
||||
MemoryTest();
|
||||
MemoryTest(const MemoryTest&);
|
||||
MemoryTest& operator=(const MemoryTest&);
|
||||
};
|
||||
}
|
||||
|
||||
Skeleton* MemoryTest::skeleton = NULL;
|
||||
|
||||
#endif /* MemoryTest_h */
|
||||
@ -1,6 +1,6 @@
|
||||
//
|
||||
// SimpleTest.h
|
||||
// TestHarness
|
||||
// spine_unit_test
|
||||
//
|
||||
// Created by Stephen Gowen on 11/9/17.
|
||||
// Copyright © 2017 Noctis Games. All rights reserved.
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "spine/Extension.h"
|
||||
|
||||
#include "SimpleTest.h"
|
||||
#include "MemoryTest.h"
|
||||
|
||||
#include "KMemory.h" // last include
|
||||
|
||||
@ -101,6 +102,7 @@ int main(int argc, char* argv[])
|
||||
#endif
|
||||
|
||||
SimpleTest::test();
|
||||
MemoryTest::test();
|
||||
|
||||
// End Timing
|
||||
time(&end_time);
|
||||
|
||||
@ -1,338 +1,339 @@
|
||||
//#include <spine/Extension.h>
|
||||
//#include "MemoryTestFixture.h"
|
||||
//#include "SpineEventMonitor.h"
|
||||
//
|
||||
//#include "KMemory.h" // last include
|
||||
//
|
||||
//#define SPINEBOY_JSON "testdata/spineboy/spineboy-ess.json"
|
||||
//#define SPINEBOY_ATLAS "testdata/spineboy/spineboy.atlas"
|
||||
//
|
||||
//#define MAX_RUN_TIME 6000 // equal to about 100 seconds of execution
|
||||
//
|
||||
//MemoryTestFixture::~MemoryTestFixture()
|
||||
//{
|
||||
// finalize();
|
||||
//}
|
||||
//
|
||||
//void MemoryTestFixture::initialize()
|
||||
//{
|
||||
// // on a Per- Fixture Basis, before Test execution
|
||||
//}
|
||||
//
|
||||
//void MemoryTestFixture::finalize()
|
||||
//{
|
||||
// // on a Per- Fixture Basis, after all tests pass/fail
|
||||
//}
|
||||
//
|
||||
//void MemoryTestFixture::setUp()
|
||||
//{
|
||||
// // Setup on Per-Test Basis
|
||||
//}
|
||||
//
|
||||
//void MemoryTestFixture::tearDown()
|
||||
//{
|
||||
// // Tear Down on Per-Test Basis
|
||||
//}
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//// Helper methods
|
||||
//static spSkeletonData* readSkeletonJsonData(const char* filename, spAtlas* atlas) {
|
||||
// spSkeletonJson* json = spSkeletonJson_create(atlas);
|
||||
// ASSERT(json != 0);
|
||||
//
|
||||
// spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, filename);
|
||||
// ASSERT(skeletonData != 0);
|
||||
//
|
||||
// spSkeletonJson_dispose(json);
|
||||
// return skeletonData;
|
||||
//}
|
||||
//
|
||||
//static void LoadSpineboyExample(spAtlas* &atlas, spSkeletonData* &skeletonData, spAnimationStateData* &stateData, spSkeleton* &skeleton, spAnimationState* &state)
|
||||
//{
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
// // Global Animation Information
|
||||
// atlas = spAtlas_createFromFile(SPINEBOY_ATLAS, 0);
|
||||
// ASSERT(atlas != 0);
|
||||
//
|
||||
// skeletonData = readSkeletonJsonData(SPINEBOY_JSON, atlas);
|
||||
// ASSERT(skeletonData != 0);
|
||||
//
|
||||
// stateData = spAnimationStateData_create(skeletonData);
|
||||
// ASSERT(stateData != 0);
|
||||
// stateData->defaultMix = 0.4f; // force mixing
|
||||
//
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
// // Animation Instance
|
||||
// skeleton = spSkeleton_create(skeletonData);
|
||||
// ASSERT(skeleton != 0);
|
||||
//
|
||||
// state = spAnimationState_create(stateData);
|
||||
// ASSERT(state != 0);
|
||||
//}
|
||||
//
|
||||
//static void DisposeAll(spSkeleton* skeleton, spAnimationState* state, spAnimationStateData* stateData, spSkeletonData* skeletonData, spAtlas* atlas)
|
||||
//{
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
// // Dispose Instance
|
||||
// spSkeleton_dispose(skeleton);
|
||||
// spAnimationState_dispose(state);
|
||||
//
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
// // Dispose Global
|
||||
// spAnimationStateData_dispose(stateData);
|
||||
// spSkeletonData_dispose(skeletonData);
|
||||
// spAtlas_dispose(atlas);
|
||||
//}
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//// Reproduce Memory leak as described in Issue #776
|
||||
//// https://github.com/EsotericSoftware/spine-runtimes/issues/776
|
||||
//void MemoryTestFixture::reproduceIssue_776()
|
||||
//{
|
||||
// spAtlas* atlas = 0;
|
||||
// spSkeletonData* skeletonData = 0;
|
||||
// spAnimationStateData* stateData = 0;
|
||||
// spSkeleton* skeleton = 0;
|
||||
// spAnimationState* state = 0;
|
||||
//
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// // Initialize Animations
|
||||
// LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
//
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
// // Run animation
|
||||
// spSkeleton_setToSetupPose(skeleton);
|
||||
// InterruptMonitor eventMonitor(state);
|
||||
// //eventMonitor.SetDebugLogging(true);
|
||||
//
|
||||
// // Interrupt the animation on this specific sequence of spEventType(s)
|
||||
// eventMonitor
|
||||
// .AddInterruptEvent(SP_ANIMATION_INTERRUPT, "jump")
|
||||
// .AddInterruptEvent(SP_ANIMATION_START);
|
||||
//
|
||||
// spAnimationState_setAnimationByName(state, 0, "walk", true);
|
||||
// spAnimationState_addAnimationByName(state, 0, "jump", false, 0.0f);
|
||||
// spAnimationState_addAnimationByName(state, 0, "run", true, 0.0f);
|
||||
// spAnimationState_addAnimationByName(state, 0, "jump", false, 3.0f);
|
||||
// spAnimationState_addAnimationByName(state, 0, "walk", true, 0.0f);
|
||||
// spAnimationState_addAnimationByName(state, 0, "idle", false, 1.0f);
|
||||
//
|
||||
// for (int i = 0; i < MAX_RUN_TIME && eventMonitor.isAnimationPlaying(); ++i) {
|
||||
// const float timeSlice = 1.0f / 60.0f;
|
||||
// spSkeleton_update(skeleton, timeSlice);
|
||||
// spAnimationState_update(state, timeSlice);
|
||||
// spAnimationState_apply(state, skeleton);
|
||||
// }
|
||||
//
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// // Cleanup Animations
|
||||
// DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
//}
|
||||
//
|
||||
//void MemoryTestFixture::reproduceIssue_777()
|
||||
//{
|
||||
// spAtlas* atlas = 0;
|
||||
// spSkeletonData* skeletonData = 0;
|
||||
// spAnimationStateData* stateData = 0;
|
||||
// spSkeleton* skeleton = 0;
|
||||
// spAnimationState* state = 0;
|
||||
//
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// // Initialize Animations
|
||||
// LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
//
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
// // Run animation
|
||||
// spSkeleton_setToSetupPose(skeleton);
|
||||
// SpineEventMonitor eventMonitor(state);
|
||||
// //eventMonitor.SetDebugLogging(true);
|
||||
//
|
||||
// // Set Animation and Play for 5 frames
|
||||
// spAnimationState_setAnimationByName(state, 0, "walk", true);
|
||||
// for (int i = 0; i < 5; ++i) {
|
||||
// const float timeSlice = 1.0f / 60.0f;
|
||||
// spSkeleton_update(skeleton, timeSlice);
|
||||
// spAnimationState_update(state, timeSlice);
|
||||
// spAnimationState_apply(state, skeleton);
|
||||
// }
|
||||
//
|
||||
// // Change animation twice in a row
|
||||
// spAnimationState_setAnimationByName(state, 0, "walk", false);
|
||||
// spAnimationState_setAnimationByName(state, 0, "run", false);
|
||||
//
|
||||
// // run normal update
|
||||
// for (int i = 0; i < 5; ++i) {
|
||||
// const float timeSlice = 1.0f / 60.0f;
|
||||
// spSkeleton_update(skeleton, timeSlice);
|
||||
// spAnimationState_update(state, timeSlice);
|
||||
// spAnimationState_apply(state, skeleton);
|
||||
// }
|
||||
//
|
||||
// // Now we'd lose mixingFrom (the first "walk" entry we set above) and should leak
|
||||
// spAnimationState_setAnimationByName(state, 0, "run", false);
|
||||
//
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// // Cleanup Animations
|
||||
// DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
//}
|
||||
//
|
||||
//spSkeleton* skeleton = 0;
|
||||
//static void spineAnimStateHandler(spAnimationState* state, int type, spTrackEntry* entry, spEvent* event)
|
||||
//{
|
||||
// if (type == SP_ANIMATION_COMPLETE)
|
||||
// {
|
||||
// spAnimationState_setAnimationByName(state, 0, "walk", false);
|
||||
// spAnimationState_update(state, 0);
|
||||
// spAnimationState_apply(state, skeleton);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//void MemoryTestFixture::reproduceIssue_Loop()
|
||||
//{
|
||||
// spAtlas* atlas = 0;
|
||||
// spSkeletonData* skeletonData = 0;
|
||||
// spAnimationStateData* stateData = 0;
|
||||
// spAnimationState* state = 0;
|
||||
//
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// // Initialize Animations
|
||||
// LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
//
|
||||
// ///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// if (state)
|
||||
// state->listener = (spAnimationStateListener)&spineAnimStateHandler;
|
||||
//
|
||||
// spAnimationState_setAnimationByName(state, 0, "walk", false);
|
||||
//
|
||||
// // run normal update
|
||||
// for (int i = 0; i < 50; ++i) {
|
||||
// const float timeSlice = 1.0f / 60.0f;
|
||||
// spSkeleton_update(skeleton, timeSlice);
|
||||
// spAnimationState_update(state, timeSlice);
|
||||
// spAnimationState_apply(state, skeleton);
|
||||
// }
|
||||
//
|
||||
// DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
//}
|
||||
//
|
||||
//void MemoryTestFixture::triangulator() {
|
||||
// spTriangulator* triangulator = spTriangulator_create();
|
||||
// spFloatArray* polygon = spFloatArray_create(16);
|
||||
// spFloatArray_add(polygon, 0);
|
||||
// spFloatArray_add(polygon, 0);
|
||||
// spFloatArray_add(polygon, 100);
|
||||
// spFloatArray_add(polygon, 0);
|
||||
// spFloatArray_add(polygon, 100);
|
||||
// spFloatArray_add(polygon, 100);
|
||||
// spFloatArray_add(polygon, 0);
|
||||
// spFloatArray_add(polygon, 100);
|
||||
//
|
||||
// spShortArray* triangles = spTriangulator_triangulate(triangulator, polygon);
|
||||
// ASSERT(triangles->size == 6);
|
||||
// ASSERT(triangles->items[0] == 3);
|
||||
// ASSERT(triangles->items[1] == 0);
|
||||
// ASSERT(triangles->items[2] == 1);
|
||||
// ASSERT(triangles->items[3] == 3);
|
||||
// ASSERT(triangles->items[4] == 1);
|
||||
// ASSERT(triangles->items[5] == 2);
|
||||
//
|
||||
// spArrayFloatArray* polys = spTriangulator_decompose(triangulator, polygon, triangles);
|
||||
// ASSERT(polys->size == 1);
|
||||
// ASSERT(polys->items[0]->size == 8);
|
||||
// ASSERT(polys->items[0]->items[0] == 0);
|
||||
// ASSERT(polys->items[0]->items[1] == 100);
|
||||
// ASSERT(polys->items[0]->items[2] == 0);
|
||||
// ASSERT(polys->items[0]->items[3] == 0);
|
||||
// ASSERT(polys->items[0]->items[4] == 100);
|
||||
// ASSERT(polys->items[0]->items[5] == 0);
|
||||
// ASSERT(polys->items[0]->items[6] == 100);
|
||||
// ASSERT(polys->items[0]->items[7] == 100);
|
||||
//
|
||||
// spFloatArray_dispose(polygon);
|
||||
// spTriangulator_dispose(triangulator);
|
||||
//}
|
||||
//
|
||||
//void MemoryTestFixture::skeletonClipper() {
|
||||
// spSkeletonClipping* clipping = spSkeletonClipping_create();
|
||||
//
|
||||
// spBoneData* boneData = spBoneData_create(0, "bone", 0);
|
||||
// spBone* bone = spBone_create(boneData, 0, 0);
|
||||
// CONST_CAST(float, bone->a) = 1;
|
||||
// CONST_CAST(float, bone->b) = 0;
|
||||
// CONST_CAST(float, bone->c) = 0;
|
||||
// CONST_CAST(float, bone->d) = 1;
|
||||
// CONST_CAST(float, bone->worldX) = 0;
|
||||
// CONST_CAST(float, bone->worldY) = 0;
|
||||
// spSlotData* slotData = spSlotData_create(0, "slot", 0);
|
||||
// spSlot* slot = spSlot_create(slotData, bone);
|
||||
// spClippingAttachment* clip = spClippingAttachment_create("clipping");
|
||||
// clip->endSlot = slotData;
|
||||
// clip->super.worldVerticesLength = 4 * 2;
|
||||
// clip->super.verticesCount = 4;
|
||||
// clip->super.vertices = MALLOC(float, 4 * 8);
|
||||
// clip->super.vertices[0] = 0;
|
||||
// clip->super.vertices[1] = 50;
|
||||
// clip->super.vertices[2] = 100;
|
||||
// clip->super.vertices[3] = 50;
|
||||
// clip->super.vertices[4] = 100;
|
||||
// clip->super.vertices[5] = 70;
|
||||
// clip->super.vertices[6] = 0;
|
||||
// clip->super.vertices[7] = 70;
|
||||
//
|
||||
// spSkeletonClipping_clipStart(clipping, slot, clip);
|
||||
//
|
||||
// spFloatArray* vertices = spFloatArray_create(16);
|
||||
// spFloatArray_add(vertices, 0);
|
||||
// spFloatArray_add(vertices, 0);
|
||||
// spFloatArray_add(vertices, 100);
|
||||
// spFloatArray_add(vertices, 0);
|
||||
// spFloatArray_add(vertices, 50);
|
||||
// spFloatArray_add(vertices, 150);
|
||||
// spFloatArray* uvs = spFloatArray_create(16);
|
||||
// spFloatArray_add(uvs, 0);
|
||||
// spFloatArray_add(uvs, 0);
|
||||
// spFloatArray_add(uvs, 1);
|
||||
// spFloatArray_add(uvs, 0);
|
||||
// spFloatArray_add(uvs, 0.5f);
|
||||
// spFloatArray_add(uvs, 1);
|
||||
// spUnsignedShortArray* indices = spUnsignedShortArray_create(16);
|
||||
// spUnsignedShortArray_add(indices, 0);
|
||||
// spUnsignedShortArray_add(indices, 1);
|
||||
// spUnsignedShortArray_add(indices, 2);
|
||||
//
|
||||
// spSkeletonClipping_clipTriangles(clipping, vertices->items, vertices->size, indices->items, indices->size, uvs->items, 2);
|
||||
//
|
||||
// float expectedVertices[8] = { 83.333328, 50.000000, 76.666664, 70.000000, 23.333334, 70.000000, 16.666672, 50.000000 };
|
||||
// ASSERT(clipping->clippedVertices->size == 8);
|
||||
// for (int i = 0; i < clipping->clippedVertices->size; i++) {
|
||||
// ASSERT(ABS(clipping->clippedVertices->items[i] - expectedVertices[i]) < 0.001);
|
||||
// }
|
||||
//
|
||||
// float expectedUVs[8] = { 0.833333f, 0.333333, 0.766667, 0.466667, 0.233333, 0.466667, 0.166667, 0.333333 };
|
||||
// ASSERT(clipping->clippedUVs->size == 8);
|
||||
// for (int i = 0; i < clipping->clippedUVs->size; i++) {
|
||||
// ASSERT(ABS(clipping->clippedUVs->items[i] - expectedUVs[i]) < 0.001);
|
||||
// }
|
||||
//
|
||||
// short expectedIndices[6] = { 0, 1, 2, 0, 2, 3 };
|
||||
// ASSERT(clipping->clippedTriangles->size == 6);
|
||||
// for (int i = 0; i < clipping->clippedTriangles->size; i++) {
|
||||
// ASSERT(clipping->clippedTriangles->items[i] == expectedIndices[i]);
|
||||
// }
|
||||
//
|
||||
// spFloatArray_dispose(vertices);
|
||||
// spFloatArray_dispose(uvs);
|
||||
// spUnsignedShortArray_dispose(indices);
|
||||
//
|
||||
// spSlotData_dispose(slotData);
|
||||
// spSlot_dispose(slot);
|
||||
// spBoneData_dispose(boneData);
|
||||
// spBone_dispose(bone);
|
||||
// _spClippingAttachment_dispose(SUPER(SUPER(clip)));
|
||||
// spSkeletonClipping_dispose(clipping);
|
||||
//}
|
||||
//
|
||||
//
|
||||
#include <spine/Extension.h>
|
||||
#include "MemoryTestFixture.h"
|
||||
#include "SpineEventMonitor.h"
|
||||
|
||||
#include "KMemory.h" // last include
|
||||
|
||||
#define SPINEBOY_JSON "testdata/spineboy/spineboy-ess.json"
|
||||
#define SPINEBOY_ATLAS "testdata/spineboy/spineboy.atlas"
|
||||
|
||||
#define MAX_RUN_TIME 6000 // equal to about 100 seconds of execution
|
||||
|
||||
MemoryTestFixture::~MemoryTestFixture()
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
|
||||
void MemoryTestFixture::initialize()
|
||||
{
|
||||
// on a Per- Fixture Basis, before Test execution
|
||||
}
|
||||
|
||||
void MemoryTestFixture::finalize()
|
||||
{
|
||||
// on a Per- Fixture Basis, after all tests pass/fail
|
||||
}
|
||||
|
||||
void MemoryTestFixture::setUp()
|
||||
{
|
||||
// Setup on Per-Test Basis
|
||||
}
|
||||
|
||||
void MemoryTestFixture::tearDown()
|
||||
{
|
||||
// Tear Down on Per-Test Basis
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Helper methods
|
||||
static spSkeletonData* readSkeletonJsonData(const char* filename, spAtlas* atlas) {
|
||||
spSkeletonJson* json = spSkeletonJson_create(atlas);
|
||||
ASSERT(json != 0);
|
||||
|
||||
spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, filename);
|
||||
ASSERT(skeletonData != 0);
|
||||
|
||||
spSkeletonJson_dispose(json);
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
static void LoadSpineboyExample(spAtlas* &atlas, spSkeletonData* &skeletonData, spAnimationStateData* &stateData, spSkeleton* &skeleton, spAnimationState* &state)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Global Animation Information
|
||||
atlas = spAtlas_createFromFile(SPINEBOY_ATLAS, 0);
|
||||
ASSERT(atlas != 0);
|
||||
|
||||
skeletonData = readSkeletonJsonData(SPINEBOY_JSON, atlas);
|
||||
ASSERT(skeletonData != 0);
|
||||
|
||||
stateData = spAnimationStateData_create(skeletonData);
|
||||
ASSERT(stateData != 0);
|
||||
stateData->defaultMix = 0.4f; // force mixing
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Animation Instance
|
||||
skeleton = spSkeleton_create(skeletonData);
|
||||
ASSERT(skeleton != 0);
|
||||
|
||||
state = spAnimationState_create(stateData);
|
||||
ASSERT(state != 0);
|
||||
}
|
||||
|
||||
static void DisposeAll(spSkeleton* skeleton, spAnimationState* state, spAnimationStateData* stateData, spSkeletonData* skeletonData, spAtlas* atlas)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Dispose Instance
|
||||
spSkeleton_dispose(skeleton);
|
||||
spAnimationState_dispose(state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Dispose Global
|
||||
spAnimationStateData_dispose(stateData);
|
||||
spSkeletonData_dispose(skeletonData);
|
||||
spAtlas_dispose(atlas);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Reproduce Memory leak as described in Issue #776
|
||||
// https://github.com/EsotericSoftware/spine-runtimes/issues/776
|
||||
void MemoryTestFixture::reproduceIssue_776()
|
||||
{
|
||||
spAtlas* atlas = 0;
|
||||
spSkeletonData* skeletonData = 0;
|
||||
spAnimationStateData* stateData = 0;
|
||||
spSkeleton* skeleton = 0;
|
||||
spAnimationState* state = 0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Initialize Animations
|
||||
LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Run animation
|
||||
spSkeleton_setToSetupPose(skeleton);
|
||||
InterruptMonitor eventMonitor(state);
|
||||
//eventMonitor.SetDebugLogging(true);
|
||||
|
||||
// Interrupt the animation on this specific sequence of spEventType(s)
|
||||
eventMonitor
|
||||
.AddInterruptEvent(SP_ANIMATION_INTERRUPT, "jump")
|
||||
.AddInterruptEvent(SP_ANIMATION_START);
|
||||
|
||||
spAnimationState_setAnimationByName(state, 0, "walk", true);
|
||||
spAnimationState_addAnimationByName(state, 0, "jump", false, 0.0f);
|
||||
spAnimationState_addAnimationByName(state, 0, "run", true, 0.0f);
|
||||
spAnimationState_addAnimationByName(state, 0, "jump", false, 3.0f);
|
||||
spAnimationState_addAnimationByName(state, 0, "walk", true, 0.0f);
|
||||
spAnimationState_addAnimationByName(state, 0, "idle", false, 1.0f);
|
||||
|
||||
for (int i = 0; i < MAX_RUN_TIME && eventMonitor.isAnimationPlaying(); ++i) {
|
||||
const float timeSlice = 1.0f / 60.0f;
|
||||
spSkeleton_update(skeleton, timeSlice);
|
||||
spAnimationState_update(state, timeSlice);
|
||||
spAnimationState_apply(state, skeleton);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Cleanup Animations
|
||||
DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
}
|
||||
|
||||
void MemoryTestFixture::reproduceIssue_777()
|
||||
{
|
||||
spAtlas* atlas = 0;
|
||||
spSkeletonData* skeletonData = 0;
|
||||
spAnimationStateData* stateData = 0;
|
||||
spSkeleton* skeleton = 0;
|
||||
spAnimationState* state = 0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Initialize Animations
|
||||
LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Run animation
|
||||
spSkeleton_setToSetupPose(skeleton);
|
||||
SpineEventMonitor eventMonitor(state);
|
||||
//eventMonitor.SetDebugLogging(true);
|
||||
|
||||
// Set Animation and Play for 5 frames
|
||||
spAnimationState_setAnimationByName(state, 0, "walk", true);
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
const float timeSlice = 1.0f / 60.0f;
|
||||
spSkeleton_update(skeleton, timeSlice);
|
||||
spAnimationState_update(state, timeSlice);
|
||||
spAnimationState_apply(state, skeleton);
|
||||
}
|
||||
|
||||
// Change animation twice in a row
|
||||
spAnimationState_setAnimationByName(state, 0, "walk", false);
|
||||
spAnimationState_setAnimationByName(state, 0, "run", false);
|
||||
|
||||
// run normal update
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
const float timeSlice = 1.0f / 60.0f;
|
||||
spSkeleton_update(skeleton, timeSlice);
|
||||
spAnimationState_update(state, timeSlice);
|
||||
spAnimationState_apply(state, skeleton);
|
||||
}
|
||||
|
||||
// Now we'd lose mixingFrom (the first "walk" entry we set above) and should leak
|
||||
spAnimationState_setAnimationByName(state, 0, "run", false);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Cleanup Animations
|
||||
DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
}
|
||||
|
||||
spSkeleton* skeleton = 0;
|
||||
static void spineAnimStateHandler(spAnimationState* state, int type, spTrackEntry* entry, spEvent* event)
|
||||
{
|
||||
if (type == SP_ANIMATION_COMPLETE)
|
||||
{
|
||||
spAnimationState_setAnimationByName(state, 0, "walk", false);
|
||||
spAnimationState_update(state, 0);
|
||||
spAnimationState_apply(state, skeleton);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryTestFixture::reproduceIssue_Loop()
|
||||
{
|
||||
spAtlas* atlas = 0;
|
||||
spSkeletonData* skeletonData = 0;
|
||||
spAnimationStateData* stateData = 0;
|
||||
spAnimationState* state = 0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Initialize Animations
|
||||
LoadSpineboyExample(atlas, skeletonData, stateData, skeleton, state);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (state)
|
||||
state->listener = (spAnimationStateListener)&spineAnimStateHandler;
|
||||
|
||||
spAnimationState_setAnimationByName(state, 0, "walk", false);
|
||||
|
||||
// run normal update
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
const float timeSlice = 1.0f / 60.0f;
|
||||
spSkeleton_update(skeleton, timeSlice);
|
||||
spAnimationState_update(state, timeSlice);
|
||||
spAnimationState_apply(state, skeleton);
|
||||
}
|
||||
|
||||
DisposeAll(skeleton, state, stateData, skeletonData, atlas);
|
||||
}
|
||||
|
||||
void MemoryTestFixture::triangulator() {
|
||||
spTriangulator* triangulator = spTriangulator_create();
|
||||
spFloatArray* polygon = spFloatArray_create(16);
|
||||
spFloatArray_add(polygon, 0);
|
||||
spFloatArray_add(polygon, 0);
|
||||
spFloatArray_add(polygon, 100);
|
||||
spFloatArray_add(polygon, 0);
|
||||
spFloatArray_add(polygon, 100);
|
||||
spFloatArray_add(polygon, 100);
|
||||
spFloatArray_add(polygon, 0);
|
||||
spFloatArray_add(polygon, 100);
|
||||
|
||||
spShortArray* triangles = spTriangulator_triangulate(triangulator, polygon);
|
||||
ASSERT(triangles->size == 6);
|
||||
ASSERT(triangles->items[0] == 3);
|
||||
ASSERT(triangles->items[1] == 0);
|
||||
ASSERT(triangles->items[2] == 1);
|
||||
ASSERT(triangles->items[3] == 3);
|
||||
ASSERT(triangles->items[4] == 1);
|
||||
ASSERT(triangles->items[5] == 2);
|
||||
|
||||
spArrayFloatArray* polys = spTriangulator_decompose(triangulator, polygon, triangles);
|
||||
ASSERT(polys->size == 1);
|
||||
ASSERT(polys->items[0]->size == 8);
|
||||
ASSERT(polys->items[0]->items[0] == 0);
|
||||
ASSERT(polys->items[0]->items[1] == 100);
|
||||
ASSERT(polys->items[0]->items[2] == 0);
|
||||
ASSERT(polys->items[0]->items[3] == 0);
|
||||
ASSERT(polys->items[0]->items[4] == 100);
|
||||
ASSERT(polys->items[0]->items[5] == 0);
|
||||
ASSERT(polys->items[0]->items[6] == 100);
|
||||
ASSERT(polys->items[0]->items[7] == 100);
|
||||
|
||||
spFloatArray_dispose(polygon);
|
||||
spTriangulator_dispose(triangulator);
|
||||
}
|
||||
|
||||
void MemoryTestFixture::skeletonClipper() {
|
||||
spSkeletonClipping* clipping = spSkeletonClipping_create();
|
||||
|
||||
spBoneData* boneData = spBoneData_create(0, "bone", 0);
|
||||
spBone* bone = spBone_create(boneData, 0, 0);
|
||||
CONST_CAST(float, bone->a) = 1;
|
||||
CONST_CAST(float, bone->b) = 0;
|
||||
CONST_CAST(float, bone->c) = 0;
|
||||
CONST_CAST(float, bone->d) = 1;
|
||||
CONST_CAST(float, bone->worldX) = 0;
|
||||
CONST_CAST(float, bone->worldY) = 0;
|
||||
spSlotData* slotData = spSlotData_create(0, "slot", 0);
|
||||
spSlot* slot = spSlot_create(slotData, bone);
|
||||
spClippingAttachment* clip = spClippingAttachment_create("clipping");
|
||||
clip->endSlot = slotData;
|
||||
clip->super.worldVerticesLength = 4 * 2;
|
||||
clip->super.verticesCount = 4;
|
||||
clip->super.vertices = MALLOC(float, 4 * 8);
|
||||
clip->super.vertices[0] = 0;
|
||||
clip->super.vertices[1] = 50;
|
||||
clip->super.vertices[2] = 100;
|
||||
clip->super.vertices[3] = 50;
|
||||
clip->super.vertices[4] = 100;
|
||||
clip->super.vertices[5] = 70;
|
||||
clip->super.vertices[6] = 0;
|
||||
clip->super.vertices[7] = 70;
|
||||
|
||||
spSkeletonClipping_clipStart(clipping, slot, clip);
|
||||
|
||||
spFloatArray* vertices = spFloatArray_create(16);
|
||||
spFloatArray_add(vertices, 0);
|
||||
spFloatArray_add(vertices, 0);
|
||||
spFloatArray_add(vertices, 100);
|
||||
spFloatArray_add(vertices, 0);
|
||||
spFloatArray_add(vertices, 50);
|
||||
spFloatArray_add(vertices, 150);
|
||||
spFloatArray* uvs = spFloatArray_create(16);
|
||||
spFloatArray_add(uvs, 0);
|
||||
spFloatArray_add(uvs, 0);
|
||||
spFloatArray_add(uvs, 1);
|
||||
spFloatArray_add(uvs, 0);
|
||||
spFloatArray_add(uvs, 0.5f);
|
||||
spFloatArray_add(uvs, 1);
|
||||
spUnsignedShortArray* indices = spUnsignedShortArray_create(16);
|
||||
spUnsignedShortArray_add(indices, 0);
|
||||
spUnsignedShortArray_add(indices, 1);
|
||||
spUnsignedShortArray_add(indices, 2);
|
||||
|
||||
spSkeletonClipping_clipTriangles(clipping, vertices->items, vertices->size, indices->items, indices->size, uvs->items, 2);
|
||||
|
||||
float expectedVertices[8] = { 83.333328, 50.000000, 76.666664, 70.000000, 23.333334, 70.000000, 16.666672, 50.000000 };
|
||||
ASSERT(clipping->clippedVertices->size == 8);
|
||||
for (int i = 0; i < clipping->clippedVertices->size; i++) {
|
||||
ASSERT(ABS(clipping->clippedVertices->items[i] - expectedVertices[i]) < 0.001);
|
||||
}
|
||||
|
||||
float expectedUVs[8] = { 0.833333f, 0.333333, 0.766667, 0.466667, 0.233333, 0.466667, 0.166667, 0.333333 };
|
||||
ASSERT(clipping->clippedUVs->size == 8);
|
||||
for (int i = 0; i < clipping->clippedUVs->size; i++) {
|
||||
ASSERT(ABS(clipping->clippedUVs->items[i] - expectedUVs[i]) < 0.001);
|
||||
}
|
||||
|
||||
short expectedIndices[6] = { 0, 1, 2, 0, 2, 3 };
|
||||
ASSERT(clipping->clippedTriangles->size == 6);
|
||||
for (int i = 0; i < clipping->clippedTriangles->size; i++) {
|
||||
ASSERT(clipping->clippedTriangles->items[i] == expectedIndices[i]);
|
||||
}
|
||||
|
||||
spFloatArray_dispose(vertices);
|
||||
spFloatArray_dispose(uvs);
|
||||
spUnsignedShortArray_dispose(indices);
|
||||
|
||||
spSlotData_dispose(slotData);
|
||||
spSlot_dispose(slot);
|
||||
spBoneData_dispose(boneData);
|
||||
spBone_dispose(bone);
|
||||
_spClippingAttachment_dispose(SUPER(SUPER(clip)));
|
||||
spSkeletonClipping_dispose(clipping);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -116,7 +116,7 @@ InterruptMonitor& InterruptMonitor::AddInterruptEvent(int theEventType, TrackEnt
|
||||
InterruptMonitor& InterruptMonitor::AddInterruptEventTrigger(const std::string & theEventTriggerName)
|
||||
{
|
||||
InterruptEvent ev;
|
||||
// ev.mEventType = SP_ANIMATION_EVENT;
|
||||
ev.mEventType = EventType_Event;
|
||||
ev.mEventName = theEventTriggerName;
|
||||
mEventStack.push_back(ev);
|
||||
return *this;
|
||||
@ -143,7 +143,7 @@ void InterruptMonitor::OnSpineAnimationStateEvent(AnimationState * state, EventT
|
||||
|
||||
inline bool InterruptMonitor::InterruptEvent::matches(AnimationState * state, EventType type, TrackEntry * trackEntry, Event * event)
|
||||
{
|
||||
// Must match spEventType {SP_ANIMATION_START, SP_ANIMATION_INTERRUPT, SP_ANIMATION_END, SP_ANIMATION_COMPLETE, SP_ANIMATION_DISPOSE, SP_ANIMATION_EVENT }
|
||||
// Must match EventType {EventType_Start, EventType_Interrupt, EventType_End, EventType_Complete, EventType_Dispose, EventType_Event }
|
||||
if (mEventType == type)
|
||||
{
|
||||
// Looking for specific TrackEntry by pointer
|
||||
|
||||
@ -162,12 +162,18 @@ namespace Spine
|
||||
void setAShearY(float inValue);
|
||||
|
||||
float getA();
|
||||
void setA(float inValue);
|
||||
float getB();
|
||||
void setB(float inValue);
|
||||
float getC();
|
||||
void setC(float inValue);
|
||||
float getD();
|
||||
void setD(float inValue);
|
||||
|
||||
float getWorldX();
|
||||
void setWorldX(float inValue);
|
||||
float getWorldY();
|
||||
void setWorldY(float inValue);
|
||||
float getWorldRotationX();
|
||||
float getWorldRotationY();
|
||||
|
||||
|
||||
@ -54,6 +54,10 @@ namespace Spine
|
||||
|
||||
bool isClipping();
|
||||
|
||||
Vector<float>& getClippedVertices();
|
||||
Vector<int>& getClippedTriangles();
|
||||
Vector<float>& getClippedUVs();
|
||||
|
||||
private:
|
||||
Triangulator _triangulator;
|
||||
Vector<float> _clippingPolygon;
|
||||
|
||||
@ -66,6 +66,7 @@ namespace Spine
|
||||
{
|
||||
clear();
|
||||
deallocate(_buffer);
|
||||
_buffer = NULL;
|
||||
|
||||
_size = inVector._size;
|
||||
_capacity = inVector._capacity;
|
||||
@ -194,6 +195,8 @@ namespace Spine
|
||||
|
||||
void setSize(size_t inValue)
|
||||
{
|
||||
assert(inValue <= _capacity);
|
||||
|
||||
_size = inValue;
|
||||
}
|
||||
|
||||
|
||||
@ -230,7 +230,7 @@ namespace Spine
|
||||
_timelineDipMix.clear();
|
||||
_timelinesRotation.clear();
|
||||
|
||||
_onAnimationEventFunc = NULL;
|
||||
_onAnimationEventFunc = dummyOnAnimationEventFunc;
|
||||
}
|
||||
|
||||
EventQueueEntry::EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event) :
|
||||
|
||||
@ -521,31 +521,61 @@ namespace Spine
|
||||
return _a;
|
||||
}
|
||||
|
||||
void Bone::setA(float inValue)
|
||||
{
|
||||
_a = inValue;
|
||||
}
|
||||
|
||||
float Bone::getB()
|
||||
{
|
||||
return _b;
|
||||
}
|
||||
|
||||
void Bone::setB(float inValue)
|
||||
{
|
||||
_b = inValue;
|
||||
}
|
||||
|
||||
float Bone::getC()
|
||||
{
|
||||
return _c;
|
||||
}
|
||||
|
||||
void Bone::setC(float inValue)
|
||||
{
|
||||
_c = inValue;
|
||||
}
|
||||
|
||||
float Bone::getD()
|
||||
{
|
||||
return _d;
|
||||
}
|
||||
|
||||
void Bone::setD(float inValue)
|
||||
{
|
||||
_d = inValue;
|
||||
}
|
||||
|
||||
float Bone::getWorldX()
|
||||
{
|
||||
return _worldX;
|
||||
}
|
||||
|
||||
void Bone::setWorldX(float inValue)
|
||||
{
|
||||
_worldX = inValue;
|
||||
}
|
||||
|
||||
float Bone::getWorldY()
|
||||
{
|
||||
return _worldY;
|
||||
}
|
||||
|
||||
void Bone::setWorldY(float inValue)
|
||||
{
|
||||
_worldY = inValue;
|
||||
}
|
||||
|
||||
float Bone::getWorldRotationX()
|
||||
{
|
||||
return MathUtil::atan2(_c, _a) * RadDeg;
|
||||
|
||||
@ -54,6 +54,7 @@ namespace Spine
|
||||
|
||||
int n = clip->getWorldVerticesLength();
|
||||
_clippingPolygon.reserve(n);
|
||||
_clippingPolygon.setSize(n);
|
||||
clip->computeWorldVertices(slot, 0, n, _clippingPolygon, 0, 2);
|
||||
makeClockwise(_clippingPolygon);
|
||||
Vector< Vector<float>* > clippingPolygons = _triangulator.decompose(_clippingPolygon, _triangulator.triangulate(_clippingPolygon));
|
||||
@ -96,7 +97,8 @@ namespace Spine
|
||||
|
||||
void SkeletonClipping::clipTriangles(Vector<float>& vertices, int verticesLength, Vector<int>& triangles, int trianglesLength, Vector<float>& uvs)
|
||||
{
|
||||
Vector<float>& clipOutput = _clipOutput, clippedVertices = _clippedVertices;
|
||||
Vector<float>& clipOutput = _clipOutput;
|
||||
Vector<float>& clippedVertices = _clippedVertices;
|
||||
Vector<int>& clippedTriangles = _clippedTriangles;
|
||||
Vector< Vector<float>* >& polygons = _clippingPolygons;
|
||||
int polygonsCount = static_cast<int>(_clippingPolygons.size());
|
||||
@ -135,7 +137,9 @@ namespace Spine
|
||||
|
||||
int clipOutputCount = clipOutputLength >> 1;
|
||||
clippedVertices.reserve(s + clipOutputCount * 2);
|
||||
clippedVertices.setSize(s + clipOutputCount * 2);
|
||||
_clippedUVs.reserve(s + clipOutputCount * 2);
|
||||
_clippedUVs.setSize(s + clipOutputCount * 2);
|
||||
for (int ii = 0; ii < clipOutputLength; ii += 2)
|
||||
{
|
||||
float x = clipOutput[ii], y = clipOutput[ii + 1];
|
||||
@ -152,6 +156,7 @@ namespace Spine
|
||||
|
||||
s = static_cast<int>(clippedTriangles.size());
|
||||
clippedTriangles.reserve(s + 3 * (clipOutputCount - 2));
|
||||
clippedTriangles.setSize(s + 3 * (clipOutputCount - 2));
|
||||
clipOutputCount--;
|
||||
for (int ii = 1; ii < clipOutputCount; ii++)
|
||||
{
|
||||
@ -165,7 +170,9 @@ namespace Spine
|
||||
else
|
||||
{
|
||||
clippedVertices.reserve(s + 3 * 2);
|
||||
clippedVertices.setSize(s + 3 * 2);
|
||||
_clippedUVs.reserve(s + 3 * 2);
|
||||
_clippedUVs.setSize(s + 3 * 2);
|
||||
clippedVertices[s] = x1;
|
||||
clippedVertices[s + 1] = y1;
|
||||
clippedVertices[s + 2] = x2;
|
||||
@ -182,6 +189,7 @@ namespace Spine
|
||||
|
||||
s = static_cast<int>(clippedTriangles.size());
|
||||
clippedTriangles.reserve(s + 3);
|
||||
clippedTriangles.setSize(s + 3);
|
||||
clippedTriangles[s] = index;
|
||||
clippedTriangles[s + 1] = index + 1;
|
||||
clippedTriangles[s + 2] = index + 2;
|
||||
@ -197,9 +205,24 @@ namespace Spine
|
||||
return _clipAttachment != NULL;
|
||||
}
|
||||
|
||||
Vector<float>& SkeletonClipping::getClippedVertices()
|
||||
{
|
||||
return _clippedVertices;
|
||||
}
|
||||
|
||||
Vector<int>& SkeletonClipping::getClippedTriangles()
|
||||
{
|
||||
return _clippedTriangles;
|
||||
}
|
||||
|
||||
Vector<float>& SkeletonClipping::getClippedUVs()
|
||||
{
|
||||
return _clippedUVs;
|
||||
}
|
||||
|
||||
bool SkeletonClipping::clip(float x1, float y1, float x2, float y2, float x3, float y3, Vector<float>& clippingArea, Vector<float>& output)
|
||||
{
|
||||
Vector<float> originalOutput = output;
|
||||
Vector<float>& originalOutput = output;
|
||||
bool clipped = false;
|
||||
|
||||
// Avoid copy at the end.
|
||||
@ -225,7 +248,7 @@ namespace Spine
|
||||
input.push_back(y1);
|
||||
output.clear();
|
||||
|
||||
Vector<float> clippingVertices = clippingArea;
|
||||
Vector<float>& clippingVertices = clippingArea;
|
||||
int clippingVerticesLast = static_cast<int>(clippingArea.size()) - 4;
|
||||
for (int i = 0; ; i += 2)
|
||||
{
|
||||
@ -233,7 +256,7 @@ namespace Spine
|
||||
float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
|
||||
float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
|
||||
|
||||
Vector<float> inputVertices = input;
|
||||
Vector<float>& inputVertices = input;
|
||||
int inputVerticesLength = static_cast<int>(input.size()) - 2, outputStart = static_cast<int>(output.size());
|
||||
for (int ii = 0; ii < inputVerticesLength; ii += 2)
|
||||
{
|
||||
@ -299,6 +322,7 @@ namespace Spine
|
||||
else
|
||||
{
|
||||
originalOutput.reserve(originalOutput.size() - 2);
|
||||
originalOutput.setSize(originalOutput.size() - 2);
|
||||
}
|
||||
|
||||
return clipped;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user