From e24b061c61bbaf539af35d488d8e811788ea15ac Mon Sep 17 00:00:00 2001 From: Stephen Gowen Date: Fri, 8 Dec 2017 14:15:30 -0500 Subject: [PATCH] Yay, now the C++ Runtime passes all the same tests as the C Runtime! --- .../spine_unit_test.xcodeproj/project.pbxproj | 2 + .../spine_unit_test/MemoryTest.h | 444 ++++++++++++ .../spine_unit_test/SimpleTest.h | 2 +- .../spine_unit_test/spine_unit_test/main.cpp | 2 + .../tests/MemoryTestFixture.cpp | 677 +++++++++--------- .../tests/SpineEventMonitor.cpp | 4 +- spine-cpp/spine-cpp/include/spine/Bone.h | 6 + .../include/spine/SkeletonClipping.h | 4 + spine-cpp/spine-cpp/include/spine/Vector.h | 3 + .../spine-cpp/src/spine/AnimationState.cpp | 2 +- spine-cpp/spine-cpp/src/spine/Bone.cpp | 30 + .../spine-cpp/src/spine/SkeletonClipping.cpp | 32 +- 12 files changed, 862 insertions(+), 346 deletions(-) create mode 100644 spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/MemoryTest.h diff --git a/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test.xcodeproj/project.pbxproj b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test.xcodeproj/project.pbxproj index 7c82f0d7e..b3171bc62 100644 --- a/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test.xcodeproj/project.pbxproj +++ b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test.xcodeproj/project.pbxproj @@ -281,6 +281,7 @@ BB6017F01FD92A5B009BD546 /* SimpleTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleTest.h; sourceTree = ""; }; BB6017FC1FD92AF3009BD546 /* SpineEventMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpineEventMonitor.h; sourceTree = ""; }; BB6017FD1FD92AF3009BD546 /* SpineEventMonitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpineEventMonitor.cpp; sourceTree = ""; }; + BBFB507F1FDAF6CD005B22B6 /* MemoryTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MemoryTest.h; sourceTree = ""; }; /* 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 = ""; diff --git a/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/MemoryTest.h b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/MemoryTest.h new file mode 100644 index 000000000..ed8705012 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/MemoryTest.h @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 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 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 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 *> 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 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 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 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 indices; + indices.reserve(16); + indices.push_back(0); + indices.push_back(1); + indices.push_back(2); + + clipping->clipTriangles(vertices, static_cast(vertices.size()), indices, static_cast(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 */ diff --git a/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/SimpleTest.h b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/SimpleTest.h index 5cdf2e3d6..5f09bba0d 100644 --- a/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/SimpleTest.h +++ b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/SimpleTest.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. diff --git a/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/main.cpp b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/main.cpp index ea1741f2f..05b091cae 100644 --- a/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/main.cpp +++ b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/main.cpp @@ -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); diff --git a/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.cpp b/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.cpp index a59822a43..d06c196e6 100755 --- a/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.cpp +++ b/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.cpp @@ -1,338 +1,339 @@ -//#include -//#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 +#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); +} + + + diff --git a/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.cpp b/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.cpp index b24f39521..9506e354c 100755 --- a/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.cpp +++ b/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.cpp @@ -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 diff --git a/spine-cpp/spine-cpp/include/spine/Bone.h b/spine-cpp/spine-cpp/include/spine/Bone.h index 6b36dd824..c7f1c7bf6 100644 --- a/spine-cpp/spine-cpp/include/spine/Bone.h +++ b/spine-cpp/spine-cpp/include/spine/Bone.h @@ -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(); diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonClipping.h b/spine-cpp/spine-cpp/include/spine/SkeletonClipping.h index 7a5cde4cd..f61e4aa7d 100644 --- a/spine-cpp/spine-cpp/include/spine/SkeletonClipping.h +++ b/spine-cpp/spine-cpp/include/spine/SkeletonClipping.h @@ -54,6 +54,10 @@ namespace Spine bool isClipping(); + Vector& getClippedVertices(); + Vector& getClippedTriangles(); + Vector& getClippedUVs(); + private: Triangulator _triangulator; Vector _clippingPolygon; diff --git a/spine-cpp/spine-cpp/include/spine/Vector.h b/spine-cpp/spine-cpp/include/spine/Vector.h index 538fab273..7c7dc3394 100644 --- a/spine-cpp/spine-cpp/include/spine/Vector.h +++ b/spine-cpp/spine-cpp/include/spine/Vector.h @@ -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; } diff --git a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp index bf9f7107f..48e2485c9 100644 --- a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp +++ b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp @@ -230,7 +230,7 @@ namespace Spine _timelineDipMix.clear(); _timelinesRotation.clear(); - _onAnimationEventFunc = NULL; + _onAnimationEventFunc = dummyOnAnimationEventFunc; } EventQueueEntry::EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event) : diff --git a/spine-cpp/spine-cpp/src/spine/Bone.cpp b/spine-cpp/spine-cpp/src/spine/Bone.cpp index 71e119c95..14ffbf850 100644 --- a/spine-cpp/spine-cpp/src/spine/Bone.cpp +++ b/spine-cpp/spine-cpp/src/spine/Bone.cpp @@ -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; diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp index 6a9f516a3..69e7a958f 100644 --- a/spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp +++ b/spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp @@ -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* > clippingPolygons = _triangulator.decompose(_clippingPolygon, _triangulator.triangulate(_clippingPolygon)); @@ -96,7 +97,8 @@ namespace Spine void SkeletonClipping::clipTriangles(Vector& vertices, int verticesLength, Vector& triangles, int trianglesLength, Vector& uvs) { - Vector& clipOutput = _clipOutput, clippedVertices = _clippedVertices; + Vector& clipOutput = _clipOutput; + Vector& clippedVertices = _clippedVertices; Vector& clippedTriangles = _clippedTriangles; Vector< Vector* >& polygons = _clippingPolygons; int polygonsCount = static_cast(_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(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(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& SkeletonClipping::getClippedVertices() + { + return _clippedVertices; + } + + Vector& SkeletonClipping::getClippedTriangles() + { + return _clippedTriangles; + } + + Vector& SkeletonClipping::getClippedUVs() + { + return _clippedUVs; + } + bool SkeletonClipping::clip(float x1, float y1, float x2, float y2, float x3, float y3, Vector& clippingArea, Vector& output) { - Vector originalOutput = output; + Vector& originalOutput = output; bool clipped = false; // Avoid copy at the end. @@ -225,7 +248,7 @@ namespace Spine input.push_back(y1); output.clear(); - Vector clippingVertices = clippingArea; + Vector& clippingVertices = clippingArea; int clippingVerticesLast = static_cast(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 inputVertices = input; + Vector& inputVertices = input; int inputVerticesLength = static_cast(input.size()) - 2, outputStart = static_cast(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;