diff --git a/spine-cpp/CMakeLists.txt b/spine-cpp/CMakeLists.txt new file mode 100644 index 000000000..b5d0c99ba --- /dev/null +++ b/spine-cpp/CMakeLists.txt @@ -0,0 +1,9 @@ +include_directories(include) +file(GLOB INCLUDES "spine-cpp/include/**/*.h") +file(GLOB SOURCES "spine-cpp/src/**/*.cpp") + +set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} -Wall -std=c++03 -pedantic -fno-exceptions -fno-rtti") +add_library(spine-cpp STATIC ${SOURCES} ${INCLUDES}) +target_include_directories(spine-cpp PUBLIC spine-cpp/include) +install(TARGETS spine-cpp DESTINATION dist/lib) +install(FILES ${INCLUDES} DESTINATION dist/include) \ No newline at end of file diff --git a/spine-cpp/LICENSE b/spine-cpp/LICENSE new file mode 100644 index 000000000..daceab94a --- /dev/null +++ b/spine-cpp/LICENSE @@ -0,0 +1,27 @@ +Spine Runtimes Software License v2.5 + +Copyright (c) 2013-2016, Esoteric Software +All rights reserved. + +You are granted a perpetual, non-exclusive, non-sublicensable, and +non-transferable license to use, install, execute, and perform the Spine +Runtimes software and derivative works solely for personal or internal +use. Without the written permission of Esoteric Software (see Section 2 of +the Spine Software License Agreement), you may not (a) modify, translate, +adapt, or develop new applications using the Spine Runtimes or otherwise +create derivative works or improvements of the Spine Runtimes or (b) remove, +delete, alter, or obscure any trademarks or any copyright, trademark, patent, +or other intellectual property or proprietary rights notices on or in the +Software, including any copy thereof. Redistributions in binary or source +form must include this license and terms. + +THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/spine-cpp/README.md b/spine-cpp/README.md new file mode 100644 index 000000000..51d1ed5a0 --- /dev/null +++ b/spine-cpp/README.md @@ -0,0 +1,39 @@ +# spine-cpp + +The spine-cpp runtime provides basic functionality to load and manipulate [Spine](http://esotericsoftware.com) skeletal animation data using C++. It does not perform rendering but can be extended to enable Spine animations for other projects that utilize C++. Note, this library uses C++03 for maximum portability and therefore does not take advantage of any C++11 or newer features such as std::unique_ptr. + +## Licensing + +This Spine Runtime may only be used for personal or internal use, typically to evaluate Spine before purchasing. If you would like to incorporate a Spine Runtime into your applications, distribute software containing a Spine Runtime, or modify a Spine Runtime, then you will need a valid [Spine license](https://esotericsoftware.com/spine-purchase). Please see the [Spine Runtimes Software License](http://esotericsoftware.com/git/spine-runtimes/blob/LICENSE) for detailed information. + +The Spine Runtimes are developed with the intent to be used with data exported from Spine. By purchasing Spine, `Section 2` of the [Spine Software License](https://esotericsoftware.com/files/license.txt) grants the right to create and distribute derivative works of the Spine Runtimes. + +## Spine version + +spine-cpp works with data exported from Spine 3.6.xx. + +spine-cpp supports all Spine features. + +## Setup + +1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it as a zip via the download button above. +2. Create a new project and import the source. + +Alternatively, the contents of the `spine-cpp/spine-cpp/src` and `spine-cpp/spine-cpp/include` directories can be copied into your project. Be sure your header search is configured to find the contents of the `spine-cpp/spine-cpp/include` directory. Note that the includes use `spine/Xxx.h`, so the `spine` directory cannot be omitted when copying the files. + +## Extension + +Extending spine-cpp requires implementing both the SpineExtension class (which has a handy default instance) and the TextureLoader class: + +Spine::SpineExtension::setInstance(Spine::DefaultSpineExtension::getInstance()); + +class MyTextureLoader : public TextureLoader +{ + virtual void load(AtlasPage& page, std::string path) { // TODO } + + virtual void unload(void* texture) { // TODO } +}; + +## Runtimes extending spine-cpp + +- Coming Soon! diff --git a/spine-cpp/spine-cpp-unit-tests/CMakeLists.txt b/spine-cpp/spine-cpp-unit-tests/CMakeLists.txt new file mode 100755 index 000000000..e08a7d3ba --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 2.8.9) +project(spine_unit_test) + +set(CMAKE_INSTALL_PREFIX "./") +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -DKANJI_MEMTRACE -DUSE_CPP11_MUTEX -std=c++11") + +######################################################### +# set includes +######################################################### +include_directories(../spine-cpp/include teamcity minicppunit tests memory) + +######################################################### +# Add Sources +######################################################### +set(MINICPP_SRC + minicppunit/MiniCppUnit.cxx +) + +set(TEAMCITY_SRC + teamcity/teamcity_cppunit.cpp + teamcity/teamcity_messages.cpp +) + +set(TEST_SRC + tests/SpineEventMonitor.cpp + tests/EmptyTestFixture.cpp + tests/C_InterfaceTestFixture.cpp + tests/CPP_InterfaceTestFixture.cpp + tests/MemoryTestFixture.cpp +) + +set(MEMLEAK_SRC + memory/KMemory.cpp + memory/KString.cpp +) + +######################################################### +# setup main project +######################################################### +add_executable(spine_unit_test main.cpp ${MINICPP_SRC} ${TEAMCITY_SRC} ${TEST_SRC} ${MEMLEAK_SRC}) +target_link_libraries(spine_unit_test spine-cpp) + + +######################################################### +# copy resources to build output directory +######################################################### +add_custom_command(TARGET spine_unit_test PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_LIST_DIR}/../../examples/spineboy/export $/testdata/spineboy) + +add_custom_command(TARGET spine_unit_test PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_LIST_DIR}/../../examples/raptor/export $/testdata/raptor) + +add_custom_command(TARGET spine_unit_test PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_LIST_DIR}/../../examples/goblins/export $/testdata/goblins) diff --git a/spine-cpp/spine-cpp-unit-tests/README.md b/spine-cpp/spine-cpp-unit-tests/README.md new file mode 100755 index 000000000..bfe38c489 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/README.md @@ -0,0 +1,67 @@ +# spine-cpp-unit-tests + +The spine-cpp-unit-tests project is to test the [Spine](http://esotericsoftware.com) skeletal animation system. It does not perform rendering. It is primarily used for regression testing and leak detection. It is designed to be run from a Continuous Integration server and to passively verify changes automatically on check-in. + +## Mini CPP Unit Testing +[MiniCppUnit](https://sourceforge.net/p/minicppunit/wiki/Home/) is a minimal unit testing framework similar to JUnit. It is used here to avoid large dependancies. + +Tests are sorted into Suites, Fixtures and Cases. There is one suite, it contains many fixtures and each fixture contains test cases. To turn off a fixture, edit "TestOptions.h". To turn off specific test cases, comment out the TEST_CASE() line in the fixture's header. + +## Memory Leak Detection +This project includes a very minimal memory leak detector. It is based roughly on the leak detector in the [Popcap Framework](https://sourceforge.net/projects/popcapframework/?source=directory), but has been modified over the years. + +## Continuous Integration +The test runner includes the ability to format output messages to signal a CI server. An example interface for [Teamcity](https://www.jetbrains.com/teamcity/) is included. To implement for another server, determine the wireformat for the messages and duplicate/edit the teamcity_messages class. [Teamcity Wire Format](https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity) + +### Trigger +Your CI server should trigger on VCS check-in. + +### CMake Build Step +The first build step for the CI server should be to run CMake on the 'spine-cpp-unit-tests' folder. Follow the usage directions below. + +### Compile Build Step +This build step should not execute if the previous step did not successfully complete. +Depending on the test agent build environment, you should build the output solution or project from the cmake step. Debug is fine. + +### Test Runner Build Step +This build step should not execute if the previous step did not successfully complete. +Again, depending on the test agent build environment, you should have produced an executable. Run this executable. + + +## Usage +Make sure [CMake](https://cmake.org/download/) is installed. + +Create a 'build' directory in the 'spine-cpp-unit-tests' folder. Then switch to that folder and execute cmake: + +mkdir build +cd build +cmake .. + +### Win32 build +msbuild spine_unit_test.sln /t:spine_unit_test /p:Configuration="Debug" /p:Platform="Win32" + + +## Licensing +This Spine Runtime may only be used for personal or internal use, typically to evaluate Spine before purchasing. If you would like to incorporate a Spine Runtime into your applications, distribute software containing a Spine Runtime, or modify a Spine Runtime, then you will need a valid [Spine license](https://esotericsoftware.com/spine-purchase). Please see the [Spine Runtimes Software License](https://github.com/EsotericSoftware/spine-runtimes/blob/master/LICENSE) for detailed information. + +The Spine Runtimes are developed with the intent to be used with data exported from Spine. By purchasing Spine, `Section 2` of the [Spine Software License](https://esotericsoftware.com/files/license.txt) grants the right to create and distribute derivative works of the Spine Runtimes. + +original "walk"": 330 +second "walk": 0d0 + +queue interrupt for original walk +queue start for second walk +drain interrupt and start + +0d0 is interrupted +0d0 is ended + +"run": 0c0 + 0d0 is interrupted + second walk becomes mixingFrom of run + 0c0 is started + + queue is drained + + first walk: 6f0 + second walk: 9c0 diff --git a/spine-cpp/spine-cpp-unit-tests/main.cpp b/spine-cpp/spine-cpp-unit-tests/main.cpp new file mode 100755 index 000000000..3cfa5fcd4 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/main.cpp @@ -0,0 +1,110 @@ +// SexyKanjiTestSuite.cpp : Defines the entry point for the console application. +// + +#include "MiniCppUnit.hxx" + +#ifdef WIN32 +#include +#else +#include +#endif // WIN32 + +#include +#include "KString.h" +#include + +#include "spine/Extension.h" + +#include "KMemory.h" // last include + +using namespace Spine; + +class KanjiSpineExtension : public DefaultSpineExtension +{ +public: + static KanjiSpineExtension* getInstance(); + + virtual ~KanjiSpineExtension(); + + virtual void* spineAlloc(size_t size, const char* file, int line); + + virtual void* spineCalloc(size_t num, size_t size, const char* file, int line); + + virtual void* spineRealloc(void* ptr, size_t size, const char* file, int line); + + virtual void spineFree(void* mem); + +protected: + KanjiSpineExtension(); +}; + +KanjiSpineExtension* KanjiSpineExtension::getInstance() +{ + static KanjiSpineExtension ret; + return &ret; +} + +KanjiSpineExtension::~KanjiSpineExtension() +{ + // Empty +} + +void* KanjiSpineExtension::spineAlloc(size_t size, const char* file, int line) +{ + return _kanjimalloc(size); +} + +void* KanjiSpineExtension::spineCalloc(size_t num, size_t size, const char* file, int line) +{ + void* ptr = _kanjimalloc(num * size, file, line); + if (ptr) + { + memset(ptr, 0, num * size); + } + + return ptr; +} + +void* KanjiSpineExtension::spineRealloc(void* ptr, size_t size, const char* file, int line) +{ + return _kanjirealloc(ptr, size); +} + +void KanjiSpineExtension::spineFree(void* mem) +{ + _kanjifree(mem); +} + +KanjiSpineExtension::KanjiSpineExtension() : DefaultSpineExtension() +{ + // Empty +} + +int main(int argc, char* argv[]) +{ + SpineExtension::setInstance(KanjiSpineExtension::getInstance()); + + // Start Timing + time_t start_time, end_time; + time(&start_time); + + /* Set working directory to current location for opening test data */ +#ifdef WIN32 + _chdir( GetFileDir(argv[0], false).c_str() ); +#else + chdir(GetFileDir(argv[0], false).c_str()); +#endif + + // Run Test Suite + if(JetBrains::underTeamcity()) gTeamCityListener.startSuite("Spine-CPP Test Suite"); + int ret_val = TestFixtureFactory::theInstance().runTests() ? 0 : -1; + if(JetBrains::underTeamcity()) gTeamCityListener.endSuite("Spine-CPP Test Suite"); + + // End Timing + time(&end_time); + double secs = difftime(end_time,start_time); + printf("\n\n%i minutes and %i seconds of your life taken from you by these tests.\n", ((int)secs) / 60, ((int)secs) % 60); + + return ret_val; +} + diff --git a/spine-cpp/spine-cpp-unit-tests/memory/KMemory.cpp b/spine-cpp/spine-cpp-unit-tests/memory/KMemory.cpp new file mode 100755 index 000000000..a21cbafba --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/memory/KMemory.cpp @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include + +#include "KString.h" + +#include "KMemory.h" // last include + +/////////////////////////////////////////////////////////////////////////////// +// +// KANJI_DUMP_LEAKED_MEM will print out the memory block that was leaked. +// This is helpful when leaked objects have string identifiers. +// +/////////////////////////////////////////////////////////////////////////////// +//#define KANJI_DUMP_LEAKED_MEM + + +/////////////////////////////////////////////////////////////////////////////// +// +// KANJI_TRACK_MEM_USAGE will print out all memory allocations. +// +/////////////////////////////////////////////////////////////////////////////// +//#define KANJI_TRACK_MEM_USAGE + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Our memory system is thread-safe, but instead of linking massive libraries, +// we attempt to use C++11 std::mutex. +#ifdef USE_CPP11_MUTEX_DISABLED +#include +typedef std::recursive_mutex KSysLock; // rentrant +struct KAutoLock { + KAutoLock(KSysLock& lock) :mLock(lock) { mLock.lock(); } // acquire + ~KAutoLock() { mLock.unlock(); } // release + + KSysLock& mLock; +}; +#else // Fallback to unsafe. don't spawn threads +typedef int KSysLock; +struct KAutoLock { + KAutoLock(KSysLock) {} // acquire + ~KAutoLock() {} // release +}; +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +struct KANJI_ALLOC_INFO +{ + size_t size; + std::string file; + int line; +}; +static bool showLeaks = false; +class KAllocMap : public std::map +{ +public: + KSysLock crit; + static bool allocMapValid; + +public: + KAllocMap() { allocMapValid = true; } + ~KAllocMap() + { + if (showLeaks) + KMemoryDumpUnfreed(); + + allocMapValid = false; + } +}; +bool KAllocMap::allocMapValid = false; +static KAllocMap allocMap; // once this static object destructs, it dumps unfreed memory + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifdef KANJI_TRACK_MEM_USAGE +void KMemoryDumpUsage(); // forward declaration +class KAllocStat +{ +public: + typedef std::map allocCount; // [size] = count + typedef std::map, allocCount> allocInfo; // [file, line] = allocCount + + allocInfo memInfo; + static bool allocMapValid; + +public: + + KAllocStat() + { + allocMapValid = true; + } + ~KAllocStat() + { + if (showLeaks) + KMemoryDumpUsage(); + + allocMapValid = false; + } + void addTrack(const char* fname, int lnum, int asize) + { + allocCount& info = memInfo[std::pair(fname, lnum)]; + info[asize]++; + } +}; +bool KAllocStat::allocMapValid = false; +static KAllocStat allocStat; +#endif // KANJI_TRACK_MEM_USAGE + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +extern void KMemoryAddTrack( void* addr, size_t asize, const char* fname, int lnum ) +{ + if (!KAllocMap::allocMapValid || asize == 0) + return; + + KAutoLock aCrit(allocMap.crit); + showLeaks = true; + + KANJI_ALLOC_INFO& info = allocMap[addr]; + info.file = fname; + info.line = lnum; + info.size = asize; + +#ifdef KANJI_TRACK_MEM_USAGE + if (KAllocStat::allocMapValid) + allocStat.addTrack(fname, lnum, asize); +#endif +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void KMemoryRemoveTrack(void* addr) +{ + if (!KAllocMap::allocMapValid) + return; + + KAutoLock aCrit(allocMap.crit); + KAllocMap::iterator anItr = allocMap.find(addr); + if (anItr != allocMap.end()) + allocMap.erase(anItr); +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void KMemoryDumpUnfreed() +{ + if (!KAllocMap::allocMapValid) + return; + + KAutoLock aCrit(allocMap.crit); // prevent modification of the map while iterating + + size_t totalSize = 0; + char buf[8192]; + + FILE* f = fopen("mem_leaks.txt", "wt"); + if (!f) + return; + + time_t aTime = time(NULL); + sprintf(buf, "Memory Leak Report for %s\n", asctime(localtime(&aTime))); + fprintf(f, "%s", buf); + KOutputDebug(DEBUGLVL, "\n"); + KOutputDebug(INFOLVL, buf); + for(KAllocMap::iterator i = allocMap.begin(); i != allocMap.end(); ++i) + { + sprintf(buf, "%s(%d) : Leak %u byte%s @0x%08X\n", i->second.file.c_str(), i->second.line, i->second.size, i->second.size > 1 ? "s" : "", (size_t)i->first); + KOutputDebug(ERRORLVL, buf); + fprintf(f, "%s", buf); + +#ifdef KANJI_DUMP_LEAKED_MEM + unsigned char* data = (unsigned char*)i->first; + int count = 0; + char hex_dump[1024]; + char ascii_dump[1024]; + + for (int index = 0; index < i->second.size; index++) + { + unsigned char _c = *data; + + if (count == 0) + sprintf(hex_dump, "\t%02X ", _c); + else + sprintf(hex_dump, "%s%02X ", hex_dump, _c); // technically, this is undefined behavior + + if ((_c < 32) || (_c > 126)) + _c = '.'; + + if (count == 7) + sprintf(ascii_dump, "%s%c ", ascii_dump, _c); + else + sprintf(ascii_dump, "%s%c", count == 0 ? "\t" : ascii_dump, _c); // technically, this is undefined behavior + + + if (++count == 16) + { + count = 0; + sprintf(buf, "%s\t%s\n", hex_dump, ascii_dump); + fprintf(f, buf); + + memset((void*)hex_dump, 0, 1024); + memset((void*)ascii_dump, 0, 1024); + } + + data++; + } + + if (count != 0) + { + fprintf(f, hex_dump); + for (int index = 0; index < 16 - count; index++) + fprintf(f, "\t"); + + fprintf(f, ascii_dump); + + for (int index = 0; index < 16 - count; index++) + fprintf(f, "."); + } + + count = 0; + fprintf(f, "\n\n"); + memset((void*)hex_dump, 0, 1024); + memset((void*)ascii_dump, 0, 1024); + +#endif // KANJI_DUMP_LEAKED_MEM + + totalSize += i->second.size; + } + + ErrorLevel lvl = (totalSize > 0) ? ERRORLVL : INFOLVL; + + sprintf(buf, "-----------------------------------------------------------\n"); + fprintf(f, "%s", buf); + KOutputDebug(lvl, buf); + sprintf(buf, "Total Unfreed: %u bytes (%luKB)\n\n", totalSize, totalSize / 1024); + KOutputDebug(lvl, buf); + fprintf(f, "%s", buf); + fclose(f); +} + +#ifdef KANJI_TRACK_MEM_USAGE +void KMemoryDumpUsage() +{ + if (!KAllocStat::allocMapValid) + return; + + char buf[8192]; + FILE* f = fopen("mem_usage.txt", "wt"); + + time_t aTime = time(NULL); + sprintf(buf, "Memory Usage Report for %s\n", asctime(localtime(&aTime))); + if (f) fprintf(f, "%s", buf); + KOutputDebug("\n"); + KOutputDebug(buf); + + for(KAllocStat::allocInfo::iterator i = allocStat.memInfo.begin(); i != allocStat.memInfo.end(); ++i) + { + int aBytesTotal = 0; + int aCallsTotal = 0; + for (KAllocStat::allocCount::iterator index = i->second.begin(); index != i->second.end(); ++index) + { + aBytesTotal += index->first; + aCallsTotal += index->second; + sprintf(buf, "%s(%d) : %d bytes (%d %s)\n", i->first.first.c_str(), i->first.second, index->first, index->second, index->second == 1 ? "call" : "calls"); + if (f) fprintf(f, "%s", buf); + KOutputDebug(buf); + } + + if (i->second.size() > 1) + { + sprintf(buf, " %s(%d) : %d KB total (%d calls)\n", i->first.first.c_str(), i->first.second, aBytesTotal / 1024, aCallsTotal); + if (f) fprintf(f, "%s", buf); + KOutputDebug(buf); + } + } + if (f) fclose(f); +} +#endif // KANJI_TRACK_MEM_USAGE + +size_t KMemoryAllocated() +{ + if (!KAllocMap::allocMapValid) + return 0; + + KAutoLock aCrit(allocMap.crit); + + size_t size = 0; + for(auto i = allocMap.begin(); i != allocMap.end(); ++i) + { + KANJI_ALLOC_INFO& info = i->second; + size += info.size; + } + return size; +} diff --git a/spine-cpp/spine-cpp-unit-tests/memory/KMemory.h b/spine-cpp/spine-cpp-unit-tests/memory/KMemory.h new file mode 100755 index 000000000..470a5db7d --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/memory/KMemory.h @@ -0,0 +1,189 @@ +#ifndef __KANJIMEMORY_H__ +#define __KANJIMEMORY_H__ + +#include +#include + +#if defined(_DEBUG) && !defined(KANJI_MEMTRACE) +#define KANJI_MEMTRACE +#endif + +#ifdef WIN32 +#pragma warning(disable : 4595) +#endif + +////////////////////////////////////////////////////////////////////////// +// HOW TO USE THIS FILE +// +// In the desired .CPP file (NOT header file), AFTER ALL of your +// #include declarations, do a #include "KMemory.h" or whatever you renamed +// this file to. It's very important that you do it only in the .cpp and +// after every other include file, otherwise it won't compile. The memory leaks +// will appear in a file called mem_leaks.txt and they will also be printed out +// in the output window when the program exits. +// +////////////////////////////////////////////////////////////////////////// + +#ifndef SAFE_DELETE +#define SAFE_DELETE(pPtr) { if(pPtr) delete pPtr; pPtr = 0; } +#endif + +#ifndef SCOPED_AUTO_SAFE_DELETE +template +class ScopedAutoDeletePointerHelper +{ +public: + ScopedAutoDeletePointerHelper(T pPtr) : _pPtr(pPtr) {} + ~ScopedAutoDeletePointerHelper() { SAFE_DELETE(_pPtr); } + + T _pPtr; +}; +#define SCOPED_AUTO_SAFE_DELETE(p) ScopedAutoDeletePointerHelper anAutoDelete##p(p); +#endif + +#ifndef SAFE_DELETE_ARRAY +#define SAFE_DELETE_ARRAY(pPtr) { if(pPtr) delete [] pPtr; pPtr = 0; } +#endif + +extern void KMemoryDumpUnfreed(); +extern size_t KMemoryAllocated(); + +#ifdef WIN32 +#define KMEM_CALLTYPE __cdecl +#else +#define KMEM_CALLTYPE +#endif + +#ifdef __APPLE__ +#define KMEM_THROWSPEC throw(std::bad_alloc) +#define KMEM_THROWS_BADALLOC +#include +#else +#define KMEM_THROWSPEC +#endif + +#if defined(KANJI_MEMTRACE) + +///////////////////////////////////////////// +// DO NOT CALL THESE TWO METHODS DIRECTLY // +///////////////////////////////////////////// + +extern void KMemoryAddTrack(void* addr, size_t asize, const char* fname, int lnum); +extern void KMemoryRemoveTrack(void* addr); + +//Replacement for the standard malloc/free, records size of allocation and the file/line number it was on +inline void* _kanjimalloc (size_t size, const char* file, int line) +{ + void* ptr = (void*)malloc(size); + KMemoryAddTrack(ptr, size, file, line); + return(ptr); +} + +inline void* _kanjimalloc (size_t size) +{ + return _kanjimalloc(size, "", 0); +} + +inline void _kanjifree (void* ptr) +{ + KMemoryRemoveTrack(ptr); + free(ptr); +} + +inline void* _kanjirealloc (void* ptr, size_t size, const char* file, int line) +{ + void* ptr2 = (void*)realloc(ptr, size); + if (ptr2) + { + KMemoryRemoveTrack(ptr); + KMemoryAddTrack(ptr2, size, file, line); + } + return ptr2; +} + +inline void* _kanjirealloc (void* ptr, size_t size) +{ + return _kanjirealloc(ptr, size, "", 0); +} + +#define kanjimalloc(size) _kanjimalloc((size), __FILE__, __LINE__) +#define kanjifree _kanjifree +#define kanjirealloc(ptr, size) _kanjirealloc(ptr, size, __FILE__, __LINE__) + +//Replacement for the standard "new" operator, records size of allocation and the file/line number it was on +inline void* KMEM_CALLTYPE operator new(size_t size, const char* file, int line) +{ + void* ptr = (void*)malloc(size); + KMemoryAddTrack(ptr, size, file, line); + return(ptr); +} + +//Same as above, but for arrays +inline void* KMEM_CALLTYPE operator new[](size_t size, const char* file, int line) +{ + void* ptr = (void*)malloc(size); + KMemoryAddTrack(ptr, size, file, line); + return(ptr); +} + + +// These single argument new operators allow vc6 apps to compile without errors +inline void* KMEM_CALLTYPE operator new(size_t size) KMEM_THROWSPEC +{ + void* ptr = (void*)malloc(size); +#ifdef KMEM_THROWS_BADALLOC + if(!ptr) throw std::bad_alloc(); +#endif + return(ptr); +} + +inline void* KMEM_CALLTYPE operator new[](size_t size) KMEM_THROWSPEC +{ + void* ptr = (void*)malloc(size); +#ifdef KMEM_THROWS_BADALLOC + if(!ptr) throw std::bad_alloc(); +#endif // KMEM_THROWS_BADALLOC + return(ptr); +} + + +//custom delete operators +inline void KMEM_CALLTYPE operator delete(void* p) throw() +{ + KMemoryRemoveTrack(p); + free(p); +} + +inline void KMEM_CALLTYPE operator delete[](void* p) throw() +{ + KMemoryRemoveTrack(p); + free(p); +} + +//needed in case in the constructor of the class we're newing, it throws an exception +inline void KMEM_CALLTYPE operator delete(void* pMem, const char* file, int line) +{ + free(pMem); +} + +inline void KMEM_CALLTYPE operator delete[](void* pMem, const char* file, int line) +{ + free(pMem); +} + +#define KDEBUG_NEW new(__FILE__, __LINE__) +#define new KDEBUG_NEW + +#else // KANJI_MEMTRACE NOT DEFINED + +#define kanjimalloc malloc +#define kanjifree free +#define kanjirealloc realloc + +inline void* _kanjimalloc(size_t size) { return malloc(size); } +inline void _kanjifree(void* ptr) { free(ptr); } +inline void* _kanjirealloc(void* ptr, size_t size) { return realloc(ptr, size); } + +#endif // KANJI_MEMTRACE + +#endif // __KANJIMEMORY_H__ \ No newline at end of file diff --git a/spine-cpp/spine-cpp-unit-tests/memory/KString.cpp b/spine-cpp/spine-cpp-unit-tests/memory/KString.cpp new file mode 100755 index 000000000..bd7aef664 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/memory/KString.cpp @@ -0,0 +1,185 @@ +#include "KString.h" +#include + +#include "MiniCppUnit.hxx" + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// String Helper + +static std::string vasprintf(const char* fmt, va_list argv) +{ + std::string result; + va_list argv_copy; // vsnprintf modifies argv, need copy +#ifndef va_copy + argv_copy = argv; +#else + va_copy(argv_copy, argv); +#endif + + int len = vsnprintf(NULL, 0, fmt, argv_copy); + if (len > 0 && len < 255) + { + // cover 90% of all calls + char str[256] = { 0 }; + int len2 = vsnprintf(str, 255, fmt, argv); + result = str; + } + else if (len > 0) + { + char* str = static_cast(alloca(len + 1)); // alloca on stack, space for null-termination + int len2 = vsnprintf(str, len + 1, fmt, argv); + result = str; + } + return result; +} + + +static void reportWarning(const std::string& warnStr) +{ + if (JetBrains::underTeamcity()) + gTeamCityListener.messages.messageWarning(warnStr); + else + fprintf(stderr, "%s", warnStr.c_str()); +} + +static void reportError(const std::string& errorStr) +{ + if (JetBrains::underTeamcity()) + gTeamCityListener.messages.messageError(errorStr); + else + fprintf(stderr, "%s", errorStr.c_str()); +} + +static void reportInfo(const std::string& infoStr) +{ + if (JetBrains::underTeamcity()) + gTeamCityListener.messages.messageNormal(infoStr); + else + fprintf(stderr, "%s", infoStr.c_str()); +} + +static void reportDebug(const std::string& debugStr) +{ + fprintf(stderr, "%s", debugStr.c_str()); +} + +static void report(ErrorLevel level, const std::string& Str) +{ + switch (level) { + case WARNLVL: reportWarning(Str); break; + case ERRORLVL: reportError(Str); break; + case INFOLVL: reportInfo(Str); break; + case DEBUGLVL: reportDebug(Str); break; + } +} + +void KOutputDebug(ErrorLevel lvl, const char* fmt ...) +{ + va_list argList; + va_start(argList, fmt); + std::string str = vasprintf(fmt, argList); + va_end(argList); + + report(lvl, str); +} + +#define K_MAX(a,b) ((a>b) ? a : b) + +std::string GetFileName(const std::string& thePath, bool noExtension) +{ + int aLastSlash = K_MAX((int)thePath.rfind('\\'), (int)thePath.rfind('/')); + + if (noExtension) + { + int aLastDot = (int)thePath.rfind('.'); + if (aLastDot > aLastSlash) + return thePath.substr(aLastSlash + 1, aLastDot - aLastSlash - 1); + } + + if (aLastSlash == -1) + return thePath; + else + return thePath.substr(aLastSlash + 1); +} + +std::string GetFileDir(const std::string& thePath, bool withSlash) +{ + int aLastSlash = K_MAX((int)thePath.rfind(('\\')), (int)thePath.rfind(('/'))); + + if (aLastSlash == -1) + return (""); + else + { + if (withSlash) + return thePath.substr(0, aLastSlash + 1); + else + return thePath.substr(0, aLastSlash); + } +} + +std::string GetFileExt(const std::string& thePath) +{ + std::string::size_type idx = thePath.find_last_of('.'); + + if (idx != std::string::npos) + return thePath.substr(idx + 1); + + return (""); +} + +/** + * g_ascii_strcasecmp: + * @s1: string to compare with @s2. + * @s2: string to compare with @s1. + * + * Compare two strings, ignoring the case of ASCII characters. + * + * Unlike the BSD strcasecmp() function, this only recognizes standard + * ASCII letters and ignores the locale, treating all non-ASCII + * bytes as if they are not letters. + * + * This function should be used only on strings that are known to be + * in encodings where the bytes corresponding to ASCII letters always + * represent themselves. This includes UTF-8 and the ISO-8859-* + * charsets, but not for instance double-byte encodings like the + * Windows Codepage 932, where the trailing bytes of double-byte + * characters include all ASCII letters. If you compare two CP932 + * strings using this function, you will get false matches. + * + * Return value: an integer less than, equal to, or greater than + * zero if @s1 is found, respectively, to be less than, + * to match, or to be greater than @s2. + **/ +static int g_ascii_compare_caseless(const char* s1, const char* s2) +{ +#define TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 'a' + 'A' : (c)) +#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) +#define g_return_val_if_fail(expr,val) { if (!(expr)) return (val); } + + int c1, c2; + + g_return_val_if_fail(s1 != NULL, 0); + g_return_val_if_fail(s2 != NULL, 0); + + while (*s1 && *s2) + { + c1 = (int)(unsigned char)TOLOWER(*s1); + c2 = (int)(unsigned char)TOLOWER(*s2); + if (c1 != c2) + return (c1 - c2); + s1++; s2++; + } + + return (((int)(unsigned char)* s1) - ((int)(unsigned char)* s2)); + +#undef g_return_val_if_fail +#undef TOUPPER +#undef TOLOWER +} + + +int CompareNoCase(const std::string & str1, const std::string & str2) +{ + return g_ascii_compare_caseless(str1.c_str(), str2.c_str()); +} diff --git a/spine-cpp/spine-cpp-unit-tests/memory/KString.h b/spine-cpp/spine-cpp-unit-tests/memory/KString.h new file mode 100755 index 000000000..32a27cb44 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/memory/KString.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +// Error reporting with levels similar to Android and are automatically forwarded to Continuous integration server +enum ErrorLevel { + WARNLVL, + ERRORLVL, + INFOLVL, + DEBUGLVL +}; + +extern void KOutputDebug(ErrorLevel lvl, const char* fmt ...); + +extern std::string GetFileName(const std::string& thePath, bool noExtension); +extern std::string GetFileDir(const std::string& thePath, bool withSlash); +extern std::string GetFileExt(const std::string& thePath); + +extern int CompareNoCase(const std::string& str1, const std::string& str2); diff --git a/spine-cpp/spine-cpp-unit-tests/minicppunit/MiniCppUnit.cxx b/spine-cpp/spine-cpp-unit-tests/minicppunit/MiniCppUnit.cxx new file mode 100755 index 000000000..f14e790fb --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/minicppunit/MiniCppUnit.cxx @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2003-2004 Pau Arumí & David García + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "MiniCppUnit.hxx" + +JetBrains::TeamcityProgressListener gTeamCityListener; +bool gUseTeamCity = false; + +#include +#include +#include + +#define MIN(a,b) ((a < b) ? a : b) +#define MAX(a,b) ((a > b) ? a : b) + +TestsListener::TestsListener() : _currentTestName(0) +{ + _executed=_failed=_exceptions=0; + gUseTeamCity = JetBrains::underTeamcity(); +} + +TestsListener& TestsListener::theInstance() +{ + static TestsListener instancia; + return instancia; +} + +std::stringstream& TestsListener::errorsLog() +{ + if (_currentTestName) + { + _log << "\n" << errmsgTag_nameOfTest() << (*_currentTestName) << "\n"; + } + return _log; +} + +std::string TestsListener::logString() +{ + std::string aRetornar = _log.str(); + _log.str(""); + return aRetornar; +} +void TestsListener::currentTestName( std::string& name) +{ + _currentTestName = &name; + + if(gUseTeamCity)gTeamCityListener.startTest(name); +} +void TestsListener::testHasRun() // started +{ + std::cout << "."; + theInstance()._executed++; +} + +void TestsListener::testHasPassed() // finished without errors +{ + if(gUseTeamCity) + gTeamCityListener.endTest(*(theInstance()._currentTestName)); +} + +void TestsListener::testHasFailed(const char* reason, const char* file, int line) +{ + if(gUseTeamCity) + { + gTeamCityListener.addFailure(JetBrains::TestFailure(*(theInstance()._currentTestName), "", JetBrains::SourceLine(file, line), reason)); + gTeamCityListener.endTest(*(theInstance()._currentTestName)); + } + + std::cout << "F"; + theInstance()._failed++; + throw TestFailedException(); +} +void TestsListener::testHasThrown() +{ + if(gUseTeamCity) + { + gTeamCityListener.addFailure(JetBrains::TestFailure(*(theInstance()._currentTestName), "", JetBrains::SourceLine(), "Exception")); + gTeamCityListener.endTest(*(theInstance()._currentTestName)); + } + std::cout << "E"; + theInstance()._exceptions++; +} +std::string TestsListener::summary() +{ + std::ostringstream os; + os << "\nSummary:\n" + << Assert::bold() << "\tExecuted Tests: " + << _executed << Assert::normal() << std::endl + << Assert::green() << "\tPassed Tests: " + << (_executed-_failed-_exceptions) + << Assert::normal() << std::endl; + if (_failed > 0) + { + os << Assert::red() << "\tFailed Tests: " + << _failed << Assert::normal() << std::endl; + } + if (_exceptions > 0) + { + os << Assert::yellow() << "\tUnexpected exceptions: " + << _exceptions << Assert::normal() << std::endl; + } + os << std::endl; + return os.str(); +} +bool TestsListener::allTestsPassed() +{ + return !theInstance()._exceptions && !theInstance()._failed; +} + + + +void Assert::assertTrue(char* strExpression, bool expression, + const char* file, int linia) +{ + if (!expression) + { + TestsListener::theInstance().errorsLog() << "\n" + << errmsgTag_testFailedIn() << file + << errmsgTag_inLine() << linia << "\n" + << errmsgTag_failedExpression() + << bold() << strExpression << normal() << "\n"; + TestsListener::theInstance().testHasFailed(strExpression, file, linia); + } +} + +void Assert::assertTrueMissatge(char* strExpression, bool expression, + const char* missatge, const char* file, int linia) +{ + if (!expression) + { + TestsListener::theInstance().errorsLog() << "\n" + << errmsgTag_testFailedIn() << file + << errmsgTag_inLine() << linia << "\n" + << errmsgTag_failedExpression() + << bold() << strExpression << "\n" + << missatge<< normal() << "\n"; + TestsListener::theInstance().testHasFailed(strExpression, file, linia); + } +} + + + +void Assert::assertEquals( const char * expected, const char * result, + const char* file, int linia ) +{ + assertEquals(std::string(expected), std::string(result), + file, linia); + +} +void Assert::assertEquals( const bool& expected, const bool& result, + const char* file, int linia ) +{ + assertEquals( + (expected?"true":"false"), + (result?"true":"false"), + file, linia); +} + +// floating point numbers comparisons taken +// from c/c++ users journal. dec 04 pag 10 +bool isNaN(double x) +{ + bool b1 = (x < 0.0); + bool b2 = (x >= 0.0); + return !(b1 || b2); +} + +double scaledEpsilon(const double& expected, const double& fuzzyEpsilon ) +{ + const double aa = fabs(expected)+1; + return (std::isinf(aa))? fuzzyEpsilon: fuzzyEpsilon * aa; +} +bool fuzzyEquals(double expected, double result, double fuzzyEpsilon) +{ + return (expected==result) || ( fabs(expected-result) <= scaledEpsilon(expected, fuzzyEpsilon) ); +} +void Assert::assertEquals( const double& expected, const double& result, + const char* file, int linia ) +{ + const double fuzzyEpsilon = 0.000001; + assertEqualsEpsilon( expected, result, fuzzyEpsilon, file, linia ); +} + +void Assert::assertEquals( const float& expected, const float& result, + const char* file, int linia ) +{ + assertEquals((double)expected, (double)result, file, linia); +} +void Assert::assertEquals( const long double& expected, const long double& result, + const char* file, int linia ) +{ + assertEquals((double)expected, (double)result, file, linia); +} +void Assert::assertEqualsEpsilon( const double& expected, const double& result, const double& epsilon, + const char* file, int linia ) +{ + if (isNaN(expected) && isNaN(result) ) return; + if (!isNaN(expected) && !isNaN(result) && fuzzyEquals(expected, result, epsilon) ) return; + + std::stringstream anError; + anError + << errmsgTag_testFailedIn() << file + << errmsgTag_inLine() << linia << "\n" + << errmsgTag_expected() + << bold() << expected << normal() << " " + << errmsgTag_butWas() + << bold() << result << normal() << "\n"; + + TestsListener::theInstance().errorsLog() << anError.str(); + + TestsListener::theInstance().testHasFailed(anError.str().c_str(), file, linia); +} + +int Assert::notEqualIndex( const std::string & one, const std::string & other ) +{ + int end = MIN(one.length(), other.length()); + for ( int index = 0; index < end; index++ ) + if (one[index] != other[index] ) + return index; + return end; +} + + +/** + * we overload the assert with string doing colored diffs + * + * MS Visual6 doesn't allow string by reference :-( + */ +void Assert::assertEquals( const std::string expected, const std::string result, + const char* file, int linia ) +{ + if(expected == result) + return; + + int indexDiferent = notEqualIndex(expected, result); + + std::stringstream anError; + anError + << file << ", linia: " << linia << "\n" + << errmsgTag_expected() << "\n" << blue() + << expected.substr(0,indexDiferent) + << green() << expected.substr(indexDiferent) + << normal() << "\n" + << errmsgTag_butWas() << blue() << "\n" + << result.substr(0,indexDiferent) + << red() << result.substr(indexDiferent) + << normal() << std::endl; + + TestsListener::theInstance().errorsLog() << anError.str(); + + TestsListener::theInstance().testHasFailed(anError.str().c_str(), file, linia); +} +void Assert::fail(const char* motiu, const char* file, int linia) +{ + TestsListener::theInstance().errorsLog() << + file << errmsgTag_inLine() << linia << "\n" << + "Reason: " << motiu << "\n"; + + TestsListener::theInstance().testHasFailed(motiu, file, linia); +} + + diff --git a/spine-cpp/spine-cpp-unit-tests/minicppunit/MiniCppUnit.hxx b/spine-cpp/spine-cpp-unit-tests/minicppunit/MiniCppUnit.hxx new file mode 100755 index 000000000..7ce3d8fd6 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/minicppunit/MiniCppUnit.hxx @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2003-2004 Pau Arum� & David Garc�a + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef MiniCppUnit_hxx +#define MiniCppUnit_hxx + +/** + * @mainpage + * miniCppUnit + * (C) 2003-2006 Pau Arumi & David Garcia + * + * @version 2.5 2006-03-14 + * - MS Visual compatibility: SConstruct ccflags, usage example, #ifdefs + * @version 2.4 2006-03-14 + * - exit test case after first failure + * - double and float comparison with fuzzy equals (using scalable epsilon) + * - have into account not a numbers + * - new ASSERT_EQUALS_EPSILON macro + * - more colors, and disabled when comiled in MS Visual + * - removed catalan location. + * - UsageExample.cxx now uses all macros and features + * @version 2.3 2006-02-13 added usage example and SConstruct + * @version 2.2 2004-11-28 code in english and tests suites + * @version 2.1 2004-11-04 char* especialization + * @version 2.0 2004-10-26 TestsFactory + * @version 1.0 2003-10-28 initial + * + * Example of use: + * + * @code + * #include "MiniCppUnit.hxx" + * class MyTests : public TestFixture + * { + * public: + * TEST_FIXTURE( MyTests ) + * { + * CAS_DE_TEST( testAddition ); + * // etc + * } + * void testAddition() + * { + * ASSERT_EQUALS( 4, 1+1+2 ); + * } + * // etc + * }; + * + * REGISTER_FIXTURE( MyTests ); + * @endcode + * @code + * int main() + * { + * return TestFixtureFactory::theInstance().runTests() ? 0 : -1; + * } + * @endcode + * Good things: + * + * - it's a tiny framework made up of two or three src files. + * => no need to install as a library + * - object oriented and makes use of several GoF patterns + * - very simple usage. Just needs to learn very few C macros + * - string asserts are simpler to use than cppunit + * - string asserts are enhanced with coloured diffs + * - concrete test classes are totally decoupled via static factory + * => no src file have to include them all. + * - it have test suite hierarchies + * - compatible with non-standard compliant VisualC6 + * (though not necessary good ;) + */ + +#include +#include +#include +#include + +#if _MSC_VER < 1300 +/** necesary for Visual 6 which don't define std::min */ +namespace std +{ + template T min(const T& a, const T& b) { return a < b ? a: b; } +} +#endif + +#include "../teamcity/teamcity_cppunit.h" + +extern JetBrains::TeamcityProgressListener gTeamCityListener; + +/** + * A singleton class. + * Receives tests results and stores messages to the test log + * for later listing. + * It's a singleton for an easy global access from the 'Asserts' + * methods but it is probably asking for a refactoring in order to limit + * access only to TestFixtures + */ +class TestsListener +{ +public: + /** accessor to the global (static) singleton instance */ + static TestsListener& theInstance(); + std::stringstream& errorsLog(); + std::string logString(); + void currentTestName( std::string& name); + static void testHasRun(); + static void testHasPassed(); + static void testHasFailed(const char* reason, const char* file, int line); + static void testHasThrown(); + /** the human readable summary of run tests*/ + std::string summary(); + /** returns wheather all run tests have passed */ + static bool allTestsPassed(); + +private: + static const char* errmsgTag_nameOfTest() { return "Test failed: "; } + + /** constructor private: force the singleton to be wellbehaved ! */ + TestsListener(); + + std::string* _currentTestName; + std::stringstream _log; + unsigned _executed; + unsigned _failed; + unsigned _exceptions; +}; + +class TestFailedException +{ +}; + +/** + * Abstract class with interface that allows run a test. That is runTest + * and name. It is implemented by TestFixture and TestCase + * + * It does the 'Component' role in the 'Composite' patten + **/ +class Test +{ +public: + virtual ~Test(){} + /** run the test: exercice the code and check results*/ + virtual void runTest() = 0; + /** the test human-readable name */ + virtual std::string name() const = 0; +}; + + +/** + * This class is just a placeholder for all assert functions --as static methods. + * It is meant for being used just by the assert macros + */ +class Assert +{ + static const char * errmsgTag_testFailedIn() { return "Test failed in "; } + static const char * errmsgTag_inLine() { return ", line: "; }; + static const char * errmsgTag_failedExpression() { return "Failed expression: "; } + static const char * errmsgTag_expected() { return "Expected: "; } + static const char * errmsgTag_butWas() { return "But was: "; } + +public: +#ifdef _MSC_VER + static const char * blue() { return ""; } + static const char * green() { return ""; } + static const char * red() { return ""; } + static const char * normal() { return ""; } + static const char * bold() { return ""; } + static const char * yellow() { return ""; } +#else + static const char * blue() { return "\033[36;1m"; } + static const char * green() { return "\033[32;1m"; } + static const char * red() { return "\033[31;1m"; } + static const char * normal() { return "\033[0m"; } + static const char * bold() { return "\033[" "1m"; } + static const char * yellow() { return "\033[93;1m"; } +#endif + template + static void assertEquals( const AType& expected, const AType& result, + const char* file="", int linia=0 ) + { + if(expected != result) + { + std::stringstream anError; + + anError + << file << ", linia: " << linia << "\n" + << errmsgTag_expected() << " " << expected << " " + << errmsgTag_butWas() << " " << result << "\n"; + + // TestsListener::theInstance().errorsLog() << anError; + + TestsListener::theInstance().testHasFailed(anError.str().c_str(), file, linia); + } + } + + static void assertTrue(char* strExpression, bool expression, + const char* file="", int linia=0); + + static void assertTrueMissatge(char* strExpression, bool expression, + const char* missatge, const char* file="", int linia=0); + + static void assertEquals( const char * expected, const char * result, + const char* file="", int linia=0 ); + + static void assertEquals( const bool& expected, const bool& result, + const char* file="", int linia=0 ); + + static void assertEquals( const double& expected, const double& result, + const char* file="", int linia=0 ); + + static void assertEquals( const float& expected, const float& result, + const char* file="", int linia=0 ); + + static void assertEquals( const long double& expected, const long double& result, + const char* file="", int linia=0 ); + + static void assertEqualsEpsilon( const double& expected, const double& result, const double& epsilon, + const char* file="", int linia=0 ); + + static int notEqualIndex( const std::string & one, const std::string & other ); + + /** + * we overload the assert with string doing colored diffs + * + * MS Visual6 doesn't allow string by reference :-( + */ + static void assertEquals( const std::string expected, const std::string result, + const char* file="", int linia=0 ); + + static void fail(const char* motiu, const char* file="", int linia=0); + + +}; + +/** + * A TestFixture is a class that contain TestCases --which corresponds to + * ConcreteTestFixture methods-- common objects uder tests, and setUp and + * tearDown methods which are automatically executed before and after each + * test case. + * + * Is the base class of ConcreteFixtures implemented by the framework user + * + * It does the 'Composite' role in the 'Composite' GoF pattern. + * Its composite children are TestCases, which wrapps the test methods. + * + * It is a template class parametrized by ConcreteTestFixture so that it can + * instantiate TestCase objects templatized with this same parameter: it needs the + * concrete class type for calling its non-static methods. + */ +template +class TestFixture : public Test +{ +protected: + + typedef ConcreteTestFixture ConcreteFixture; + typedef void(ConcreteTestFixture::*TestCaseMethod)(); + + /** + * Wrapper for the test methods of concrete TestFixtures. + * + * Makes the 'Leave' role in the 'Composite' GoF pattern because can't be + * be a composition of other tests. + * + * It's also a case of 'Command' pattern because it encapsules in an object + * certain functionality whose execution depends on some deferred entity. + */ + class TestCase : public Test + { + public: + TestCase(ConcreteFixture* parent, TestCaseMethod method, const std::string & name) : + _parent(parent), + _testCaseMethod(method), + _name(name) + { + } + /** calls TestFixture method. setUp and tearDown methods are called by + * its parent TestFixture (in its runTest method). + * it is robust to unexpected exceptions (throw) */ + void runTest() + { + TestsListener::theInstance().testHasRun(); + TestsListener::theInstance().currentTestName(_name); + try + { + (_parent->*_testCaseMethod)(); + TestsListener::theInstance().testHasPassed(); + } + catch( std::exception& error ) + { + TestsListener::theInstance().testHasThrown(); + TestsListener::theInstance().errorsLog() + << "std::exception catched by MiniCppUnit: \n" + << "what() : " + << Assert::yellow() << error.what() + << Assert::normal() << "\n"; + } + catch ( TestFailedException& ) //just for skiping current test case + { + // the assert() calls testHasFailed() + } + catch(...) + { + TestsListener::theInstance().testHasThrown(); + TestsListener::theInstance().errorsLog() + << "non standard exception catched by MiniCppUnit.\n"; + } + } + + /** the TestFixture method hame */ + std::string name() const + { + return _name; + } + + private: + ConcreteFixture* _parent; + TestCaseMethod _testCaseMethod; + std::string _name; + }; + //------------- end of class TestCase ---------------------------- + +private: + + typedef std::list TestCases; + TestCases _testCases; + std::string _name; + + void testsList() const + { + std::cout << "\n+ " << name() << "\n"; + for( TestCases::const_iterator it=_testCases.begin(); + it!=_testCases.end(); it++ ) + std::cout << " - "<< (*it)->name() << "\n"; + } + + +public: + virtual void setUp() {} + virtual void tearDown() {} + + std::string name() const + { + return _name; + }; + + TestFixture(const std::string& name="A text fixture") : _name(name) + { + } + + void afegeixCasDeTest(ConcreteFixture* parent, TestCaseMethod method, const char* name) + { + TestCase* casDeTest = new TestCase(parent, method, _name + "::" + name); + _testCases.push_back( casDeTest ); + } + /** calls each test after setUp and tearDown TestFixture methods */ + void runTest() + { + testsList(); + TestCases::iterator it; + for( it=_testCases.begin(); it!=_testCases.end(); it++) + { + setUp(); + (*it)->runTest(); + tearDown(); + } + } + /** TestCase that wrapps TestFixture methods are dynamically created and owned by + * the TestFixture. So here we clean it up*/ + virtual ~TestFixture() + { + TestCases::iterator it; + for( it =_testCases.begin(); it!=_testCases.end(); it++) + delete (*it); + } +}; + + +/** + * This class is aimed to hold a creator method for each concrete TestFixture + */ +class TestFixtureFactory +{ +private: + /** Well behaved singleton: + * Don't allow instantiation apart from theInstance(), so private ctr.*/ + TestFixtureFactory() + { + } + typedef Test* (*FixtureCreator)(); + std::list _creators; +public: + /** Accessor to the (static) singleton instance */ + static TestFixtureFactory& theInstance() + { + static TestFixtureFactory theFactory; + return theFactory; + } + bool runTests() + { + std::list::iterator it; + for(it=_creators.begin(); it!=_creators.end(); it++) + { + FixtureCreator creator = *it; + Test* test = creator(); + test->runTest(); + delete test; + } + std::string errors = TestsListener::theInstance().logString(); + if (errors!="") std::cout << "\n\nError Details:\n" << errors; + std::cout << TestsListener::theInstance().summary(); + + return TestsListener::theInstance().allTestsPassed(); + } + void addFixtureCreator(FixtureCreator creator) + { + _creators.push_back( creator ); + } + +}; + +/** + * Macro a usar despr�s de cada classe de test + */ +#define REGISTER_FIXTURE( ConcreteTestFixture ) \ +\ +Test* Creador##ConcreteTestFixture() { return new ConcreteTestFixture; } \ +\ +class Registrador##ConcreteTestFixture \ +{ \ +public: \ + Registrador##ConcreteTestFixture() \ + { \ + TestFixtureFactory::theInstance().addFixtureCreator( \ + Creador##ConcreteTestFixture); \ + } \ +}; \ +static Registrador##ConcreteTestFixture estatic##ConcreteTestFixture; + + +/** + * Assert macros to use in test methods. An assert is a test condition + * we want to check. + */ +#define ASSERT_EQUALS( expected, result) \ + Assert::assertEquals( expected, result, __FILE__, __LINE__ ); + +#define ASSERT_EQUALS_EPSILON( expected, result, epsilon) \ + Assert::assertEqualsEpsilon( expected, result, epsilon, __FILE__, __LINE__ ); + +#define ASSERT( exp ) \ + Assert::assertTrue(#exp, exp, __FILE__, __LINE__); + +#define ASSERT_MESSAGE( exp, message ) \ + Assert::assertTrueMissatge(#exp, exp, message, __FILE__, __LINE__); + +#define FAIL( why ) \ + Assert::fail(#why, __FILE__, __LINE__); + +/** + * Macros that allows to write the constructor of the concrete TestFixture. + * What the constructor does is agregate a wrapper for each test case (method) + * As easy to write as this: + * + * @code + * class MyTests : public TestFixture + * { + * public: + * TEST_FIXTURE( MyTests ) + * { + * TEST_CASE( test ); + * // etc + * } + * void test() + * { + * ASSERT_EQUALS( 4, 1+1+2 ); + * } + * @endcode + */ + +#define TEST_FIXTURE( ConcreteFixture ) \ + ConcreteFixture() : TestFixture( #ConcreteFixture ) + +#define TEST_CASE( methodName ) \ + afegeixCasDeTest( this, &ConcreteFixture::methodName, #methodName ); + + + + + +#endif // MiniCppUnit_hxx 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 new file mode 100644 index 000000000..b3171bc62 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test.xcodeproj/project.pbxproj @@ -0,0 +1,831 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + BB4E38561FD9C85600709FF2 /* spineboy-ess.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = BB4E38541FD9C85600709FF2 /* spineboy-ess.json */; }; + BB4E38571FD9C85600709FF2 /* spineboy.atlas in CopyFiles */ = {isa = PBXBuildFile; fileRef = BB4E38551FD9C85600709FF2 /* spineboy.atlas */; }; + BB4E385A1FD9C86400709FF2 /* raptor.atlas in CopyFiles */ = {isa = PBXBuildFile; fileRef = BB4E38581FD9C86400709FF2 /* raptor.atlas */; }; + BB4E385B1FD9C86400709FF2 /* raptor-pro.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = BB4E38591FD9C86400709FF2 /* raptor-pro.json */; }; + BB4E385E1FD9C87700709FF2 /* goblins-pro.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = BB4E385C1FD9C87600709FF2 /* goblins-pro.json */; }; + BB4E385F1FD9C87700709FF2 /* goblins.atlas in CopyFiles */ = {isa = PBXBuildFile; fileRef = BB4E385D1FD9C87700709FF2 /* goblins.atlas */; }; + BB6017131FD9289A009BD546 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017121FD9289A009BD546 /* main.cpp */; }; + BB6017A21FD928AC009BD546 /* Attachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017671FD928AC009BD546 /* Attachment.cpp */; }; + BB6017A31FD928AC009BD546 /* Skeleton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017681FD928AC009BD546 /* Skeleton.cpp */; }; + BB6017A41FD928AC009BD546 /* TranslateTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017691FD928AC009BD546 /* TranslateTimeline.cpp */; }; + BB6017A51FD928AC009BD546 /* Extension.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60176A1FD928AC009BD546 /* Extension.cpp */; }; + BB6017A61FD928AC009BD546 /* Updatable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60176B1FD928AC009BD546 /* Updatable.cpp */; }; + BB6017A71FD928AC009BD546 /* Bone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60176C1FD928AC009BD546 /* Bone.cpp */; }; + BB6017A81FD928AC009BD546 /* AtlasAttachmentLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60176D1FD928AC009BD546 /* AtlasAttachmentLoader.cpp */; }; + BB6017A91FD928AC009BD546 /* EventTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60176E1FD928AC009BD546 /* EventTimeline.cpp */; }; + BB6017AA1FD928AC009BD546 /* PathConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60176F1FD928AC009BD546 /* PathConstraint.cpp */; }; + BB6017AB1FD928AC009BD546 /* VertexAttachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017701FD928AC009BD546 /* VertexAttachment.cpp */; }; + BB6017AC1FD928AC009BD546 /* TextureLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017711FD928AC009BD546 /* TextureLoader.cpp */; }; + BB6017AD1FD928AC009BD546 /* SkeletonData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017721FD928AC009BD546 /* SkeletonData.cpp */; }; + BB6017AE1FD928AC009BD546 /* TransformConstraintTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017731FD928AC009BD546 /* TransformConstraintTimeline.cpp */; }; + BB6017AF1FD928AC009BD546 /* IkConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017741FD928AC009BD546 /* IkConstraint.cpp */; }; + BB6017B01FD928AC009BD546 /* CurveTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017751FD928AC009BD546 /* CurveTimeline.cpp */; }; + BB6017B11FD928AC009BD546 /* AnimationStateData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017761FD928AC009BD546 /* AnimationStateData.cpp */; }; + BB6017B21FD928AC009BD546 /* Constraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017771FD928AC009BD546 /* Constraint.cpp */; }; + BB6017B31FD928AC009BD546 /* BoundingBoxAttachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017781FD928AC009BD546 /* BoundingBoxAttachment.cpp */; }; + BB6017B41FD928AC009BD546 /* PathAttachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017791FD928AC009BD546 /* PathAttachment.cpp */; }; + BB6017B51FD928AC009BD546 /* MeshAttachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60177A1FD928AC009BD546 /* MeshAttachment.cpp */; }; + BB6017B61FD928AC009BD546 /* TransformConstraint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60177B1FD928AC009BD546 /* TransformConstraint.cpp */; }; + BB6017B71FD928AC009BD546 /* Skin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60177C1FD928AC009BD546 /* Skin.cpp */; }; + BB6017B81FD928AC009BD546 /* RTTI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60177D1FD928AC009BD546 /* RTTI.cpp */; }; + BB6017B91FD928AC009BD546 /* MathUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60177E1FD928AC009BD546 /* MathUtil.cpp */; }; + BB6017BA1FD928AC009BD546 /* IkConstraintData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60177F1FD928AC009BD546 /* IkConstraintData.cpp */; }; + BB6017BB1FD928AC009BD546 /* Atlas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017801FD928AC009BD546 /* Atlas.cpp */; }; + BB6017BC1FD928AC009BD546 /* ClippingAttachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017811FD928AC009BD546 /* ClippingAttachment.cpp */; }; + BB6017BD1FD928AC009BD546 /* PathConstraintData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017821FD928AC009BD546 /* PathConstraintData.cpp */; }; + BB6017BE1FD928AC009BD546 /* Timeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017831FD928AC009BD546 /* Timeline.cpp */; }; + BB6017BF1FD928AC009BD546 /* SkeletonBinary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017841FD928AC009BD546 /* SkeletonBinary.cpp */; }; + BB6017C01FD928AC009BD546 /* ScaleTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017851FD928AC009BD546 /* ScaleTimeline.cpp */; }; + BB6017C11FD928AC009BD546 /* LinkedMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017861FD928AC009BD546 /* LinkedMesh.cpp */; }; + BB6017C21FD928AC009BD546 /* PointAttachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017871FD928AC009BD546 /* PointAttachment.cpp */; }; + BB6017C31FD928AC009BD546 /* RegionAttachment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017881FD928AC009BD546 /* RegionAttachment.cpp */; }; + BB6017C41FD928AC009BD546 /* DeformTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017891FD928AC009BD546 /* DeformTimeline.cpp */; }; + BB6017C51FD928AC009BD546 /* Animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60178A1FD928AC009BD546 /* Animation.cpp */; }; + BB6017C61FD928AC009BD546 /* AttachmentLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60178B1FD928AC009BD546 /* AttachmentLoader.cpp */; }; + BB6017C71FD928AC009BD546 /* DrawOrderTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60178C1FD928AC009BD546 /* DrawOrderTimeline.cpp */; }; + BB6017C81FD928AC009BD546 /* AttachmentTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60178D1FD928AC009BD546 /* AttachmentTimeline.cpp */; }; + BB6017C91FD928AC009BD546 /* EventData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60178E1FD928AC009BD546 /* EventData.cpp */; }; + BB6017CA1FD928AC009BD546 /* PathConstraintSpacingTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60178F1FD928AC009BD546 /* PathConstraintSpacingTimeline.cpp */; }; + BB6017CB1FD928AC009BD546 /* PathConstraintPositionTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017901FD928AC009BD546 /* PathConstraintPositionTimeline.cpp */; }; + BB6017CC1FD928AC009BD546 /* TransformConstraintData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017911FD928AC009BD546 /* TransformConstraintData.cpp */; }; + BB6017CD1FD928AC009BD546 /* SkeletonClipping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017921FD928AC009BD546 /* SkeletonClipping.cpp */; }; + BB6017CE1FD928AC009BD546 /* TwoColorTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017931FD928AC009BD546 /* TwoColorTimeline.cpp */; }; + BB6017CF1FD928AC009BD546 /* Slot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017941FD928AC009BD546 /* Slot.cpp */; }; + BB6017D01FD928AC009BD546 /* AnimationState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017951FD928AC009BD546 /* AnimationState.cpp */; }; + BB6017D11FD928AC009BD546 /* SkeletonJson.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017961FD928AC009BD546 /* SkeletonJson.cpp */; }; + BB6017D21FD928AC009BD546 /* BoneData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017971FD928AC009BD546 /* BoneData.cpp */; }; + BB6017D31FD928AC009BD546 /* IkConstraintTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017981FD928AC009BD546 /* IkConstraintTimeline.cpp */; }; + BB6017D41FD928AC009BD546 /* Event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017991FD928AC009BD546 /* Event.cpp */; }; + BB6017D51FD928AC009BD546 /* RotateTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60179A1FD928AC009BD546 /* RotateTimeline.cpp */; }; + BB6017D61FD928AC009BD546 /* PathConstraintMixTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60179B1FD928AC009BD546 /* PathConstraintMixTimeline.cpp */; }; + BB6017D71FD928AC009BD546 /* Triangulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60179C1FD928AC009BD546 /* Triangulator.cpp */; }; + BB6017D81FD928AC009BD546 /* Json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60179D1FD928AC009BD546 /* Json.cpp */; }; + BB6017D91FD928AC009BD546 /* SkeletonBounds.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60179E1FD928AC009BD546 /* SkeletonBounds.cpp */; }; + BB6017DA1FD928AC009BD546 /* SlotData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB60179F1FD928AC009BD546 /* SlotData.cpp */; }; + BB6017DB1FD928AC009BD546 /* ShearTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017A01FD928AC009BD546 /* ShearTimeline.cpp */; }; + BB6017DC1FD928AC009BD546 /* ColorTimeline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017A11FD928AC009BD546 /* ColorTimeline.cpp */; }; + BB6017E21FD928F7009BD546 /* KMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017E01FD928F6009BD546 /* KMemory.cpp */; }; + BB6017E31FD928F7009BD546 /* KString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017E11FD928F6009BD546 /* KString.cpp */; }; + BB6017E71FD929D0009BD546 /* MiniCppUnit.cxx in Sources */ = {isa = PBXBuildFile; fileRef = BB6017E61FD929D0009BD546 /* MiniCppUnit.cxx */; }; + BB6017EE1FD929F4009BD546 /* teamcity_cppunit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017EA1FD929F4009BD546 /* teamcity_cppunit.cpp */; }; + BB6017EF1FD929F4009BD546 /* teamcity_messages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017ED1FD929F4009BD546 /* teamcity_messages.cpp */; }; + BB6018081FD92AF4009BD546 /* SpineEventMonitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB6017FD1FD92AF3009BD546 /* SpineEventMonitor.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BB4E38471FD9C72600709FF2 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = testdata/spineboy; + dstSubfolderSpec = 16; + files = ( + BB4E38561FD9C85600709FF2 /* spineboy-ess.json in CopyFiles */, + BB4E38571FD9C85600709FF2 /* spineboy.atlas in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB4E384A1FD9C79D00709FF2 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = testdata/raptor; + dstSubfolderSpec = 16; + files = ( + BB4E385A1FD9C86400709FF2 /* raptor.atlas in CopyFiles */, + BB4E385B1FD9C86400709FF2 /* raptor-pro.json in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB4E384B1FD9C79E00709FF2 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = testdata/goblins; + dstSubfolderSpec = 16; + files = ( + BB4E385E1FD9C87700709FF2 /* goblins-pro.json in CopyFiles */, + BB4E385F1FD9C87700709FF2 /* goblins.atlas in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB60170D1FD9289A009BD546 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + BB4E38541FD9C85600709FF2 /* spineboy-ess.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "spineboy-ess.json"; path = "../../../examples/spineboy/export/spineboy-ess.json"; sourceTree = ""; }; + BB4E38551FD9C85600709FF2 /* spineboy.atlas */ = {isa = PBXFileReference; lastKnownFileType = text; name = spineboy.atlas; path = ../../../examples/spineboy/export/spineboy.atlas; sourceTree = ""; }; + BB4E38581FD9C86400709FF2 /* raptor.atlas */ = {isa = PBXFileReference; lastKnownFileType = text; name = raptor.atlas; path = ../../../examples/raptor/export/raptor.atlas; sourceTree = ""; }; + BB4E38591FD9C86400709FF2 /* raptor-pro.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "raptor-pro.json"; path = "../../../examples/raptor/export/raptor-pro.json"; sourceTree = ""; }; + BB4E385C1FD9C87600709FF2 /* goblins-pro.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "goblins-pro.json"; path = "../../../examples/goblins/export/goblins-pro.json"; sourceTree = ""; }; + BB4E385D1FD9C87700709FF2 /* goblins.atlas */ = {isa = PBXFileReference; lastKnownFileType = text; name = goblins.atlas; path = ../../../examples/goblins/export/goblins.atlas; sourceTree = ""; }; + BB60170F1FD9289A009BD546 /* spine_unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = spine_unit_test; sourceTree = BUILT_PRODUCTS_DIR; }; + BB6017121FD9289A009BD546 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + BB60171C1FD928AC009BD546 /* DeformTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DeformTimeline.h; sourceTree = ""; }; + BB60171D1FD928AC009BD546 /* Animation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Animation.h; sourceTree = ""; }; + BB60171E1FD928AC009BD546 /* EventData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventData.h; sourceTree = ""; }; + BB60171F1FD928AC009BD546 /* SlotData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SlotData.h; sourceTree = ""; }; + BB6017201FD928AC009BD546 /* PathConstraintMixTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathConstraintMixTimeline.h; sourceTree = ""; }; + BB6017211FD928AC009BD546 /* SkeletonClipping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonClipping.h; sourceTree = ""; }; + BB6017221FD928AC009BD546 /* Pool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Pool.h; sourceTree = ""; }; + BB6017231FD928AC009BD546 /* TimelineType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimelineType.h; sourceTree = ""; }; + BB6017241FD928AC009BD546 /* TextureLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextureLoader.h; sourceTree = ""; }; + BB6017251FD928AC009BD546 /* PositionMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PositionMode.h; sourceTree = ""; }; + BB6017261FD928AC009BD546 /* RTTI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RTTI.h; sourceTree = ""; }; + BB6017271FD928AC009BD546 /* PathAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathAttachment.h; sourceTree = ""; }; + BB6017281FD928AC009BD546 /* MixDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MixDirection.h; sourceTree = ""; }; + BB6017291FD928AC009BD546 /* CurveTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CurveTimeline.h; sourceTree = ""; }; + BB60172A1FD928AC009BD546 /* PointAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PointAttachment.h; sourceTree = ""; }; + BB60172B1FD928AC009BD546 /* Event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Event.h; sourceTree = ""; }; + BB60172C1FD928AC009BD546 /* Bone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Bone.h; sourceTree = ""; }; + BB60172D1FD928AC009BD546 /* Atlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atlas.h; sourceTree = ""; }; + BB60172E1FD928AC009BD546 /* DrawOrderTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawOrderTimeline.h; sourceTree = ""; }; + BB60172F1FD928AC009BD546 /* TransformConstraintTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformConstraintTimeline.h; sourceTree = ""; }; + BB6017301FD928AC009BD546 /* IkConstraintTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IkConstraintTimeline.h; sourceTree = ""; }; + BB6017311FD928AC009BD546 /* VertexAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VertexAttachment.h; sourceTree = ""; }; + BB6017321FD928AC009BD546 /* AttachmentType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttachmentType.h; sourceTree = ""; }; + BB6017331FD928AC009BD546 /* RotateMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RotateMode.h; sourceTree = ""; }; + BB6017341FD928AC009BD546 /* ClippingAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClippingAttachment.h; sourceTree = ""; }; + BB6017351FD928AC009BD546 /* PathConstraintPositionTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathConstraintPositionTimeline.h; sourceTree = ""; }; + BB6017361FD928AC009BD546 /* RotateTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RotateTimeline.h; sourceTree = ""; }; + BB6017371FD928AC009BD546 /* Triangulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Triangulator.h; sourceTree = ""; }; + BB6017381FD928AC009BD546 /* RegionAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegionAttachment.h; sourceTree = ""; }; + BB6017391FD928AC009BD546 /* Attachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Attachment.h; sourceTree = ""; }; + BB60173A1FD928AC009BD546 /* HashMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HashMap.h; sourceTree = ""; }; + BB60173B1FD928AC009BD546 /* TransformConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformConstraint.h; sourceTree = ""; }; + BB60173C1FD928AC009BD546 /* TransformMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformMode.h; sourceTree = ""; }; + BB60173D1FD928AC009BD546 /* SkeletonJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonJson.h; sourceTree = ""; }; + BB60173E1FD928AC009BD546 /* IkConstraintData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IkConstraintData.h; sourceTree = ""; }; + BB60173F1FD928AC009BD546 /* MixPose.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MixPose.h; sourceTree = ""; }; + BB6017401FD928AC009BD546 /* AnimationStateData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnimationStateData.h; sourceTree = ""; }; + BB6017411FD928AC009BD546 /* TwoColorTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TwoColorTimeline.h; sourceTree = ""; }; + BB6017421FD928AC009BD546 /* Skeleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Skeleton.h; sourceTree = ""; }; + BB6017431FD928AC009BD546 /* ColorTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorTimeline.h; sourceTree = ""; }; + BB6017441FD928AC009BD546 /* SpacingMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpacingMode.h; sourceTree = ""; }; + BB6017451FD928AC009BD546 /* Vertices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Vertices.h; sourceTree = ""; }; + BB6017461FD928AC009BD546 /* Constraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constraint.h; sourceTree = ""; }; + BB6017471FD928AC009BD546 /* LinkedMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinkedMesh.h; sourceTree = ""; }; + BB6017481FD928AC009BD546 /* ShearTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShearTimeline.h; sourceTree = ""; }; + BB6017491FD928AC009BD546 /* Json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Json.h; sourceTree = ""; }; + BB60174A1FD928AC009BD546 /* AttachmentLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttachmentLoader.h; sourceTree = ""; }; + BB60174B1FD928AC009BD546 /* Skin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Skin.h; sourceTree = ""; }; + BB60174C1FD928AC009BD546 /* AttachmentTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttachmentTimeline.h; sourceTree = ""; }; + BB60174D1FD928AC009BD546 /* SkeletonBinary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonBinary.h; sourceTree = ""; }; + BB60174E1FD928AC009BD546 /* SkeletonData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonData.h; sourceTree = ""; }; + BB60174F1FD928AC009BD546 /* ContainerUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContainerUtil.h; sourceTree = ""; }; + BB6017501FD928AC009BD546 /* PathConstraintData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathConstraintData.h; sourceTree = ""; }; + BB6017511FD928AC009BD546 /* Updatable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Updatable.h; sourceTree = ""; }; + BB6017521FD928AC009BD546 /* TransformConstraintData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformConstraintData.h; sourceTree = ""; }; + BB6017531FD928AC009BD546 /* Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Extension.h; sourceTree = ""; }; + BB6017541FD928AC009BD546 /* BlendMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlendMode.h; sourceTree = ""; }; + BB6017551FD928AC009BD546 /* PathConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathConstraint.h; sourceTree = ""; }; + BB6017561FD928AC009BD546 /* PathConstraintSpacingTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathConstraintSpacingTimeline.h; sourceTree = ""; }; + BB6017571FD928AC009BD546 /* ScaleTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScaleTimeline.h; sourceTree = ""; }; + BB6017581FD928AC009BD546 /* IkConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IkConstraint.h; sourceTree = ""; }; + BB6017591FD928AC009BD546 /* BoundingBoxAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BoundingBoxAttachment.h; sourceTree = ""; }; + BB60175A1FD928AC009BD546 /* MathUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MathUtil.h; sourceTree = ""; }; + BB60175B1FD928AC009BD546 /* Vector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Vector.h; sourceTree = ""; }; + BB60175C1FD928AC009BD546 /* SkeletonBounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonBounds.h; sourceTree = ""; }; + BB60175D1FD928AC009BD546 /* Timeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timeline.h; sourceTree = ""; }; + BB60175E1FD928AC009BD546 /* Slot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Slot.h; sourceTree = ""; }; + BB60175F1FD928AC009BD546 /* BoneData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BoneData.h; sourceTree = ""; }; + BB6017601FD928AC009BD546 /* TranslateTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TranslateTimeline.h; sourceTree = ""; }; + BB6017611FD928AC009BD546 /* AnimationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnimationState.h; sourceTree = ""; }; + BB6017621FD928AC009BD546 /* MeshAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeshAttachment.h; sourceTree = ""; }; + BB6017631FD928AC009BD546 /* AtlasAttachmentLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AtlasAttachmentLoader.h; sourceTree = ""; }; + BB6017641FD928AC009BD546 /* EventTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventTimeline.h; sourceTree = ""; }; + BB6017671FD928AC009BD546 /* Attachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Attachment.cpp; sourceTree = ""; }; + BB6017681FD928AC009BD546 /* Skeleton.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Skeleton.cpp; sourceTree = ""; }; + BB6017691FD928AC009BD546 /* TranslateTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TranslateTimeline.cpp; sourceTree = ""; }; + BB60176A1FD928AC009BD546 /* Extension.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Extension.cpp; sourceTree = ""; }; + BB60176B1FD928AC009BD546 /* Updatable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Updatable.cpp; sourceTree = ""; }; + BB60176C1FD928AC009BD546 /* Bone.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Bone.cpp; sourceTree = ""; }; + BB60176D1FD928AC009BD546 /* AtlasAttachmentLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AtlasAttachmentLoader.cpp; sourceTree = ""; }; + BB60176E1FD928AC009BD546 /* EventTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventTimeline.cpp; sourceTree = ""; }; + BB60176F1FD928AC009BD546 /* PathConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathConstraint.cpp; sourceTree = ""; }; + BB6017701FD928AC009BD546 /* VertexAttachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VertexAttachment.cpp; sourceTree = ""; }; + BB6017711FD928AC009BD546 /* TextureLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureLoader.cpp; sourceTree = ""; }; + BB6017721FD928AC009BD546 /* SkeletonData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkeletonData.cpp; sourceTree = ""; }; + BB6017731FD928AC009BD546 /* TransformConstraintTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TransformConstraintTimeline.cpp; sourceTree = ""; }; + BB6017741FD928AC009BD546 /* IkConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IkConstraint.cpp; sourceTree = ""; }; + BB6017751FD928AC009BD546 /* CurveTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CurveTimeline.cpp; sourceTree = ""; }; + BB6017761FD928AC009BD546 /* AnimationStateData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AnimationStateData.cpp; sourceTree = ""; }; + BB6017771FD928AC009BD546 /* Constraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Constraint.cpp; sourceTree = ""; }; + BB6017781FD928AC009BD546 /* BoundingBoxAttachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BoundingBoxAttachment.cpp; sourceTree = ""; }; + BB6017791FD928AC009BD546 /* PathAttachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathAttachment.cpp; sourceTree = ""; }; + BB60177A1FD928AC009BD546 /* MeshAttachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MeshAttachment.cpp; sourceTree = ""; }; + BB60177B1FD928AC009BD546 /* TransformConstraint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TransformConstraint.cpp; sourceTree = ""; }; + BB60177C1FD928AC009BD546 /* Skin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Skin.cpp; sourceTree = ""; }; + BB60177D1FD928AC009BD546 /* RTTI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RTTI.cpp; sourceTree = ""; }; + BB60177E1FD928AC009BD546 /* MathUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathUtil.cpp; sourceTree = ""; }; + BB60177F1FD928AC009BD546 /* IkConstraintData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IkConstraintData.cpp; sourceTree = ""; }; + BB6017801FD928AC009BD546 /* Atlas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atlas.cpp; sourceTree = ""; }; + BB6017811FD928AC009BD546 /* ClippingAttachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClippingAttachment.cpp; sourceTree = ""; }; + BB6017821FD928AC009BD546 /* PathConstraintData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathConstraintData.cpp; sourceTree = ""; }; + BB6017831FD928AC009BD546 /* Timeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timeline.cpp; sourceTree = ""; }; + BB6017841FD928AC009BD546 /* SkeletonBinary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkeletonBinary.cpp; sourceTree = ""; }; + BB6017851FD928AC009BD546 /* ScaleTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScaleTimeline.cpp; sourceTree = ""; }; + BB6017861FD928AC009BD546 /* LinkedMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LinkedMesh.cpp; sourceTree = ""; }; + BB6017871FD928AC009BD546 /* PointAttachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PointAttachment.cpp; sourceTree = ""; }; + BB6017881FD928AC009BD546 /* RegionAttachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegionAttachment.cpp; sourceTree = ""; }; + BB6017891FD928AC009BD546 /* DeformTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DeformTimeline.cpp; sourceTree = ""; }; + BB60178A1FD928AC009BD546 /* Animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Animation.cpp; sourceTree = ""; }; + BB60178B1FD928AC009BD546 /* AttachmentLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AttachmentLoader.cpp; sourceTree = ""; }; + BB60178C1FD928AC009BD546 /* DrawOrderTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawOrderTimeline.cpp; sourceTree = ""; }; + BB60178D1FD928AC009BD546 /* AttachmentTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AttachmentTimeline.cpp; sourceTree = ""; }; + BB60178E1FD928AC009BD546 /* EventData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventData.cpp; sourceTree = ""; }; + BB60178F1FD928AC009BD546 /* PathConstraintSpacingTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathConstraintSpacingTimeline.cpp; sourceTree = ""; }; + BB6017901FD928AC009BD546 /* PathConstraintPositionTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathConstraintPositionTimeline.cpp; sourceTree = ""; }; + BB6017911FD928AC009BD546 /* TransformConstraintData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TransformConstraintData.cpp; sourceTree = ""; }; + BB6017921FD928AC009BD546 /* SkeletonClipping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkeletonClipping.cpp; sourceTree = ""; }; + BB6017931FD928AC009BD546 /* TwoColorTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TwoColorTimeline.cpp; sourceTree = ""; }; + BB6017941FD928AC009BD546 /* Slot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Slot.cpp; sourceTree = ""; }; + BB6017951FD928AC009BD546 /* AnimationState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AnimationState.cpp; sourceTree = ""; }; + BB6017961FD928AC009BD546 /* SkeletonJson.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkeletonJson.cpp; sourceTree = ""; }; + BB6017971FD928AC009BD546 /* BoneData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BoneData.cpp; sourceTree = ""; }; + BB6017981FD928AC009BD546 /* IkConstraintTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IkConstraintTimeline.cpp; sourceTree = ""; }; + BB6017991FD928AC009BD546 /* Event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Event.cpp; sourceTree = ""; }; + BB60179A1FD928AC009BD546 /* RotateTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RotateTimeline.cpp; sourceTree = ""; }; + BB60179B1FD928AC009BD546 /* PathConstraintMixTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathConstraintMixTimeline.cpp; sourceTree = ""; }; + BB60179C1FD928AC009BD546 /* Triangulator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Triangulator.cpp; sourceTree = ""; }; + BB60179D1FD928AC009BD546 /* Json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Json.cpp; sourceTree = ""; }; + BB60179E1FD928AC009BD546 /* SkeletonBounds.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkeletonBounds.cpp; sourceTree = ""; }; + BB60179F1FD928AC009BD546 /* SlotData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SlotData.cpp; sourceTree = ""; }; + BB6017A01FD928AC009BD546 /* ShearTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShearTimeline.cpp; sourceTree = ""; }; + BB6017A11FD928AC009BD546 /* ColorTimeline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ColorTimeline.cpp; sourceTree = ""; }; + BB6017DE1FD928F6009BD546 /* KMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KMemory.h; sourceTree = ""; }; + BB6017DF1FD928F6009BD546 /* KString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KString.h; sourceTree = ""; }; + BB6017E01FD928F6009BD546 /* KMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KMemory.cpp; sourceTree = ""; }; + BB6017E11FD928F6009BD546 /* KString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KString.cpp; sourceTree = ""; }; + BB6017E51FD929D0009BD546 /* MiniCppUnit.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MiniCppUnit.hxx; sourceTree = ""; }; + BB6017E61FD929D0009BD546 /* MiniCppUnit.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MiniCppUnit.cxx; sourceTree = ""; }; + BB6017E91FD929F4009BD546 /* teamcity_cppunit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = teamcity_cppunit.h; sourceTree = ""; }; + BB6017EA1FD929F4009BD546 /* teamcity_cppunit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = teamcity_cppunit.cpp; sourceTree = ""; }; + BB6017EB1FD929F4009BD546 /* teamcity_messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = teamcity_messages.h; sourceTree = ""; }; + BB6017EC1FD929F4009BD546 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.txt; sourceTree = ""; }; + BB6017ED1FD929F4009BD546 /* teamcity_messages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = teamcity_messages.cpp; sourceTree = ""; }; + 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 */ + BB60170C1FD9289A009BD546 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BB6017061FD9289A009BD546 = { + isa = PBXGroup; + children = ( + BB4E385C1FD9C87600709FF2 /* goblins-pro.json */, + BB4E385D1FD9C87700709FF2 /* goblins.atlas */, + BB4E38591FD9C86400709FF2 /* raptor-pro.json */, + BB4E38581FD9C86400709FF2 /* raptor.atlas */, + BB4E38541FD9C85600709FF2 /* spineboy-ess.json */, + BB4E38551FD9C85600709FF2 /* spineboy.atlas */, + BB6017F11FD92AF3009BD546 /* tests */, + BB6017E81FD929F4009BD546 /* teamcity */, + BB6017E41FD929D0009BD546 /* minicppunit */, + BB6017DD1FD928F6009BD546 /* memory */, + BB6017191FD928AC009BD546 /* spine-cpp */, + BB6017111FD9289A009BD546 /* spine_unit_test */, + BB6017101FD9289A009BD546 /* Products */, + ); + sourceTree = ""; + }; + BB6017101FD9289A009BD546 /* Products */ = { + isa = PBXGroup; + children = ( + BB60170F1FD9289A009BD546 /* spine_unit_test */, + ); + name = Products; + sourceTree = ""; + }; + BB6017111FD9289A009BD546 /* spine_unit_test */ = { + isa = PBXGroup; + children = ( + BB6017121FD9289A009BD546 /* main.cpp */, + BB6017F01FD92A5B009BD546 /* SimpleTest.h */, + BBFB507F1FDAF6CD005B22B6 /* MemoryTest.h */, + ); + path = spine_unit_test; + sourceTree = ""; + }; + BB6017191FD928AC009BD546 /* spine-cpp */ = { + isa = PBXGroup; + children = ( + BB60171A1FD928AC009BD546 /* include */, + BB6017651FD928AC009BD546 /* src */, + ); + name = "spine-cpp"; + path = "../../spine-cpp"; + sourceTree = ""; + }; + BB60171A1FD928AC009BD546 /* include */ = { + isa = PBXGroup; + children = ( + BB60171B1FD928AC009BD546 /* spine */, + ); + path = include; + sourceTree = ""; + }; + BB60171B1FD928AC009BD546 /* spine */ = { + isa = PBXGroup; + children = ( + BB60171D1FD928AC009BD546 /* Animation.h */, + BB6017611FD928AC009BD546 /* AnimationState.h */, + BB6017401FD928AC009BD546 /* AnimationStateData.h */, + BB60172D1FD928AC009BD546 /* Atlas.h */, + BB6017631FD928AC009BD546 /* AtlasAttachmentLoader.h */, + BB6017391FD928AC009BD546 /* Attachment.h */, + BB60174A1FD928AC009BD546 /* AttachmentLoader.h */, + BB60174C1FD928AC009BD546 /* AttachmentTimeline.h */, + BB6017321FD928AC009BD546 /* AttachmentType.h */, + BB6017541FD928AC009BD546 /* BlendMode.h */, + BB60172C1FD928AC009BD546 /* Bone.h */, + BB60175F1FD928AC009BD546 /* BoneData.h */, + BB6017591FD928AC009BD546 /* BoundingBoxAttachment.h */, + BB6017341FD928AC009BD546 /* ClippingAttachment.h */, + BB6017431FD928AC009BD546 /* ColorTimeline.h */, + BB6017461FD928AC009BD546 /* Constraint.h */, + BB60174F1FD928AC009BD546 /* ContainerUtil.h */, + BB6017291FD928AC009BD546 /* CurveTimeline.h */, + BB60171C1FD928AC009BD546 /* DeformTimeline.h */, + BB60172E1FD928AC009BD546 /* DrawOrderTimeline.h */, + BB60172B1FD928AC009BD546 /* Event.h */, + BB60171E1FD928AC009BD546 /* EventData.h */, + BB6017641FD928AC009BD546 /* EventTimeline.h */, + BB6017531FD928AC009BD546 /* Extension.h */, + BB60173A1FD928AC009BD546 /* HashMap.h */, + BB6017581FD928AC009BD546 /* IkConstraint.h */, + BB60173E1FD928AC009BD546 /* IkConstraintData.h */, + BB6017301FD928AC009BD546 /* IkConstraintTimeline.h */, + BB6017491FD928AC009BD546 /* Json.h */, + BB6017471FD928AC009BD546 /* LinkedMesh.h */, + BB60175A1FD928AC009BD546 /* MathUtil.h */, + BB6017621FD928AC009BD546 /* MeshAttachment.h */, + BB6017281FD928AC009BD546 /* MixDirection.h */, + BB60173F1FD928AC009BD546 /* MixPose.h */, + BB6017271FD928AC009BD546 /* PathAttachment.h */, + BB6017551FD928AC009BD546 /* PathConstraint.h */, + BB6017501FD928AC009BD546 /* PathConstraintData.h */, + BB6017201FD928AC009BD546 /* PathConstraintMixTimeline.h */, + BB6017351FD928AC009BD546 /* PathConstraintPositionTimeline.h */, + BB6017561FD928AC009BD546 /* PathConstraintSpacingTimeline.h */, + BB60172A1FD928AC009BD546 /* PointAttachment.h */, + BB6017221FD928AC009BD546 /* Pool.h */, + BB6017251FD928AC009BD546 /* PositionMode.h */, + BB6017381FD928AC009BD546 /* RegionAttachment.h */, + BB6017331FD928AC009BD546 /* RotateMode.h */, + BB6017361FD928AC009BD546 /* RotateTimeline.h */, + BB6017261FD928AC009BD546 /* RTTI.h */, + BB6017571FD928AC009BD546 /* ScaleTimeline.h */, + BB6017481FD928AC009BD546 /* ShearTimeline.h */, + BB6017421FD928AC009BD546 /* Skeleton.h */, + BB60174D1FD928AC009BD546 /* SkeletonBinary.h */, + BB60175C1FD928AC009BD546 /* SkeletonBounds.h */, + BB6017211FD928AC009BD546 /* SkeletonClipping.h */, + BB60174E1FD928AC009BD546 /* SkeletonData.h */, + BB60173D1FD928AC009BD546 /* SkeletonJson.h */, + BB60174B1FD928AC009BD546 /* Skin.h */, + BB60175E1FD928AC009BD546 /* Slot.h */, + BB60171F1FD928AC009BD546 /* SlotData.h */, + BB6017441FD928AC009BD546 /* SpacingMode.h */, + BB6017241FD928AC009BD546 /* TextureLoader.h */, + BB60175D1FD928AC009BD546 /* Timeline.h */, + BB6017231FD928AC009BD546 /* TimelineType.h */, + BB60173B1FD928AC009BD546 /* TransformConstraint.h */, + BB6017521FD928AC009BD546 /* TransformConstraintData.h */, + BB60172F1FD928AC009BD546 /* TransformConstraintTimeline.h */, + BB60173C1FD928AC009BD546 /* TransformMode.h */, + BB6017601FD928AC009BD546 /* TranslateTimeline.h */, + BB6017371FD928AC009BD546 /* Triangulator.h */, + BB6017411FD928AC009BD546 /* TwoColorTimeline.h */, + BB6017511FD928AC009BD546 /* Updatable.h */, + BB60175B1FD928AC009BD546 /* Vector.h */, + BB6017311FD928AC009BD546 /* VertexAttachment.h */, + BB6017451FD928AC009BD546 /* Vertices.h */, + ); + path = spine; + sourceTree = ""; + }; + BB6017651FD928AC009BD546 /* src */ = { + isa = PBXGroup; + children = ( + BB6017661FD928AC009BD546 /* spine */, + ); + path = src; + sourceTree = ""; + }; + BB6017661FD928AC009BD546 /* spine */ = { + isa = PBXGroup; + children = ( + BB60178A1FD928AC009BD546 /* Animation.cpp */, + BB6017951FD928AC009BD546 /* AnimationState.cpp */, + BB6017761FD928AC009BD546 /* AnimationStateData.cpp */, + BB6017801FD928AC009BD546 /* Atlas.cpp */, + BB60176D1FD928AC009BD546 /* AtlasAttachmentLoader.cpp */, + BB6017671FD928AC009BD546 /* Attachment.cpp */, + BB60178B1FD928AC009BD546 /* AttachmentLoader.cpp */, + BB60178D1FD928AC009BD546 /* AttachmentTimeline.cpp */, + BB60176C1FD928AC009BD546 /* Bone.cpp */, + BB6017971FD928AC009BD546 /* BoneData.cpp */, + BB6017781FD928AC009BD546 /* BoundingBoxAttachment.cpp */, + BB6017811FD928AC009BD546 /* ClippingAttachment.cpp */, + BB6017A11FD928AC009BD546 /* ColorTimeline.cpp */, + BB6017771FD928AC009BD546 /* Constraint.cpp */, + BB6017751FD928AC009BD546 /* CurveTimeline.cpp */, + BB6017891FD928AC009BD546 /* DeformTimeline.cpp */, + BB60178C1FD928AC009BD546 /* DrawOrderTimeline.cpp */, + BB6017991FD928AC009BD546 /* Event.cpp */, + BB60178E1FD928AC009BD546 /* EventData.cpp */, + BB60176E1FD928AC009BD546 /* EventTimeline.cpp */, + BB60176A1FD928AC009BD546 /* Extension.cpp */, + BB6017741FD928AC009BD546 /* IkConstraint.cpp */, + BB60177F1FD928AC009BD546 /* IkConstraintData.cpp */, + BB6017981FD928AC009BD546 /* IkConstraintTimeline.cpp */, + BB60179D1FD928AC009BD546 /* Json.cpp */, + BB6017861FD928AC009BD546 /* LinkedMesh.cpp */, + BB60177E1FD928AC009BD546 /* MathUtil.cpp */, + BB60177A1FD928AC009BD546 /* MeshAttachment.cpp */, + BB6017791FD928AC009BD546 /* PathAttachment.cpp */, + BB60176F1FD928AC009BD546 /* PathConstraint.cpp */, + BB6017821FD928AC009BD546 /* PathConstraintData.cpp */, + BB60179B1FD928AC009BD546 /* PathConstraintMixTimeline.cpp */, + BB6017901FD928AC009BD546 /* PathConstraintPositionTimeline.cpp */, + BB60178F1FD928AC009BD546 /* PathConstraintSpacingTimeline.cpp */, + BB6017871FD928AC009BD546 /* PointAttachment.cpp */, + BB6017881FD928AC009BD546 /* RegionAttachment.cpp */, + BB60179A1FD928AC009BD546 /* RotateTimeline.cpp */, + BB60177D1FD928AC009BD546 /* RTTI.cpp */, + BB6017851FD928AC009BD546 /* ScaleTimeline.cpp */, + BB6017A01FD928AC009BD546 /* ShearTimeline.cpp */, + BB6017681FD928AC009BD546 /* Skeleton.cpp */, + BB6017841FD928AC009BD546 /* SkeletonBinary.cpp */, + BB60179E1FD928AC009BD546 /* SkeletonBounds.cpp */, + BB6017921FD928AC009BD546 /* SkeletonClipping.cpp */, + BB6017721FD928AC009BD546 /* SkeletonData.cpp */, + BB6017961FD928AC009BD546 /* SkeletonJson.cpp */, + BB60177C1FD928AC009BD546 /* Skin.cpp */, + BB6017941FD928AC009BD546 /* Slot.cpp */, + BB60179F1FD928AC009BD546 /* SlotData.cpp */, + BB6017711FD928AC009BD546 /* TextureLoader.cpp */, + BB6017831FD928AC009BD546 /* Timeline.cpp */, + BB60177B1FD928AC009BD546 /* TransformConstraint.cpp */, + BB6017911FD928AC009BD546 /* TransformConstraintData.cpp */, + BB6017731FD928AC009BD546 /* TransformConstraintTimeline.cpp */, + BB6017691FD928AC009BD546 /* TranslateTimeline.cpp */, + BB60179C1FD928AC009BD546 /* Triangulator.cpp */, + BB6017931FD928AC009BD546 /* TwoColorTimeline.cpp */, + BB60176B1FD928AC009BD546 /* Updatable.cpp */, + BB6017701FD928AC009BD546 /* VertexAttachment.cpp */, + ); + path = spine; + sourceTree = ""; + }; + BB6017DD1FD928F6009BD546 /* memory */ = { + isa = PBXGroup; + children = ( + BB6017DE1FD928F6009BD546 /* KMemory.h */, + BB6017DF1FD928F6009BD546 /* KString.h */, + BB6017E01FD928F6009BD546 /* KMemory.cpp */, + BB6017E11FD928F6009BD546 /* KString.cpp */, + ); + name = memory; + path = ../memory; + sourceTree = ""; + }; + BB6017E41FD929D0009BD546 /* minicppunit */ = { + isa = PBXGroup; + children = ( + BB6017E51FD929D0009BD546 /* MiniCppUnit.hxx */, + BB6017E61FD929D0009BD546 /* MiniCppUnit.cxx */, + ); + name = minicppunit; + path = ../minicppunit; + sourceTree = ""; + }; + BB6017E81FD929F4009BD546 /* teamcity */ = { + isa = PBXGroup; + children = ( + BB6017E91FD929F4009BD546 /* teamcity_cppunit.h */, + BB6017EA1FD929F4009BD546 /* teamcity_cppunit.cpp */, + BB6017EB1FD929F4009BD546 /* teamcity_messages.h */, + BB6017EC1FD929F4009BD546 /* README.txt */, + BB6017ED1FD929F4009BD546 /* teamcity_messages.cpp */, + ); + name = teamcity; + path = ../teamcity; + sourceTree = ""; + }; + BB6017F11FD92AF3009BD546 /* tests */ = { + isa = PBXGroup; + children = ( + BB6017FC1FD92AF3009BD546 /* SpineEventMonitor.h */, + BB6017FD1FD92AF3009BD546 /* SpineEventMonitor.cpp */, + ); + name = tests; + path = ../tests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BB60170E1FD9289A009BD546 /* spine_unit_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = BB6017161FD9289A009BD546 /* Build configuration list for PBXNativeTarget "spine_unit_test" */; + buildPhases = ( + BB60170B1FD9289A009BD546 /* Sources */, + BB60170C1FD9289A009BD546 /* Frameworks */, + BB60170D1FD9289A009BD546 /* CopyFiles */, + BB4E38471FD9C72600709FF2 /* CopyFiles */, + BB4E384A1FD9C79D00709FF2 /* CopyFiles */, + BB4E384B1FD9C79E00709FF2 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = spine_unit_test; + productName = spine_unit_test; + productReference = BB60170F1FD9289A009BD546 /* spine_unit_test */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BB6017071FD9289A009BD546 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Noctis Games"; + TargetAttributes = { + BB60170E1FD9289A009BD546 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = BB60170A1FD9289A009BD546 /* Build configuration list for PBXProject "spine_unit_test" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = BB6017061FD9289A009BD546; + productRefGroup = BB6017101FD9289A009BD546 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BB60170E1FD9289A009BD546 /* spine_unit_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + BB60170B1FD9289A009BD546 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BB6017CB1FD928AC009BD546 /* PathConstraintPositionTimeline.cpp in Sources */, + BB6017CD1FD928AC009BD546 /* SkeletonClipping.cpp in Sources */, + BB6017B51FD928AC009BD546 /* MeshAttachment.cpp in Sources */, + BB6017C01FD928AC009BD546 /* ScaleTimeline.cpp in Sources */, + BB6017D01FD928AC009BD546 /* AnimationState.cpp in Sources */, + BB6017A31FD928AC009BD546 /* Skeleton.cpp in Sources */, + BB6017BA1FD928AC009BD546 /* IkConstraintData.cpp in Sources */, + BB6017EE1FD929F4009BD546 /* teamcity_cppunit.cpp in Sources */, + BB6017CA1FD928AC009BD546 /* PathConstraintSpacingTimeline.cpp in Sources */, + BB6017C91FD928AC009BD546 /* EventData.cpp in Sources */, + BB6017B41FD928AC009BD546 /* PathAttachment.cpp in Sources */, + BB6017A91FD928AC009BD546 /* EventTimeline.cpp in Sources */, + BB6017A21FD928AC009BD546 /* Attachment.cpp in Sources */, + BB6017C81FD928AC009BD546 /* AttachmentTimeline.cpp in Sources */, + BB6017E21FD928F7009BD546 /* KMemory.cpp in Sources */, + BB6017E71FD929D0009BD546 /* MiniCppUnit.cxx in Sources */, + BB6017DA1FD928AC009BD546 /* SlotData.cpp in Sources */, + BB6017BE1FD928AC009BD546 /* Timeline.cpp in Sources */, + BB6017BF1FD928AC009BD546 /* SkeletonBinary.cpp in Sources */, + BB6017131FD9289A009BD546 /* main.cpp in Sources */, + BB6017B21FD928AC009BD546 /* Constraint.cpp in Sources */, + BB6017A41FD928AC009BD546 /* TranslateTimeline.cpp in Sources */, + BB6017A71FD928AC009BD546 /* Bone.cpp in Sources */, + BB6017DC1FD928AC009BD546 /* ColorTimeline.cpp in Sources */, + BB6017AE1FD928AC009BD546 /* TransformConstraintTimeline.cpp in Sources */, + BB6017BB1FD928AC009BD546 /* Atlas.cpp in Sources */, + BB6017C21FD928AC009BD546 /* PointAttachment.cpp in Sources */, + BB6017C11FD928AC009BD546 /* LinkedMesh.cpp in Sources */, + BB6017A61FD928AC009BD546 /* Updatable.cpp in Sources */, + BB6017DB1FD928AC009BD546 /* ShearTimeline.cpp in Sources */, + BB6017C31FD928AC009BD546 /* RegionAttachment.cpp in Sources */, + BB6017D71FD928AC009BD546 /* Triangulator.cpp in Sources */, + BB6017EF1FD929F4009BD546 /* teamcity_messages.cpp in Sources */, + BB6017D61FD928AC009BD546 /* PathConstraintMixTimeline.cpp in Sources */, + BB6017CC1FD928AC009BD546 /* TransformConstraintData.cpp in Sources */, + BB6017C71FD928AC009BD546 /* DrawOrderTimeline.cpp in Sources */, + BB6017B61FD928AC009BD546 /* TransformConstraint.cpp in Sources */, + BB6017BC1FD928AC009BD546 /* ClippingAttachment.cpp in Sources */, + BB6017B81FD928AC009BD546 /* RTTI.cpp in Sources */, + BB6017A51FD928AC009BD546 /* Extension.cpp in Sources */, + BB6017C41FD928AC009BD546 /* DeformTimeline.cpp in Sources */, + BB6017A81FD928AC009BD546 /* AtlasAttachmentLoader.cpp in Sources */, + BB6017AA1FD928AC009BD546 /* PathConstraint.cpp in Sources */, + BB6017B71FD928AC009BD546 /* Skin.cpp in Sources */, + BB6017D21FD928AC009BD546 /* BoneData.cpp in Sources */, + BB6017C61FD928AC009BD546 /* AttachmentLoader.cpp in Sources */, + BB6017CF1FD928AC009BD546 /* Slot.cpp in Sources */, + BB6017B91FD928AC009BD546 /* MathUtil.cpp in Sources */, + BB6017B11FD928AC009BD546 /* AnimationStateData.cpp in Sources */, + BB6018081FD92AF4009BD546 /* SpineEventMonitor.cpp in Sources */, + BB6017E31FD928F7009BD546 /* KString.cpp in Sources */, + BB6017D41FD928AC009BD546 /* Event.cpp in Sources */, + BB6017D81FD928AC009BD546 /* Json.cpp in Sources */, + BB6017CE1FD928AC009BD546 /* TwoColorTimeline.cpp in Sources */, + BB6017AD1FD928AC009BD546 /* SkeletonData.cpp in Sources */, + BB6017D11FD928AC009BD546 /* SkeletonJson.cpp in Sources */, + BB6017D31FD928AC009BD546 /* IkConstraintTimeline.cpp in Sources */, + BB6017AF1FD928AC009BD546 /* IkConstraint.cpp in Sources */, + BB6017AC1FD928AC009BD546 /* TextureLoader.cpp in Sources */, + BB6017C51FD928AC009BD546 /* Animation.cpp in Sources */, + BB6017B01FD928AC009BD546 /* CurveTimeline.cpp in Sources */, + BB6017BD1FD928AC009BD546 /* PathConstraintData.cpp in Sources */, + BB6017D91FD928AC009BD546 /* SkeletonBounds.cpp in Sources */, + BB6017B31FD928AC009BD546 /* BoundingBoxAttachment.cpp in Sources */, + BB6017D51FD928AC009BD546 /* RotateTimeline.cpp in Sources */, + BB6017AB1FD928AC009BD546 /* VertexAttachment.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + BB6017141FD9289A009BD546 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + BB6017151FD9289A009BD546 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + BB6017171FD9289A009BD546 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../../spine-cpp/include\""; + OTHER_LDFLAGS = "-DKANJI_MEMTRACE"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + BB6017181FD9289A009BD546 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + HEADER_SEARCH_PATHS = "\"$(SRCROOT)/../../spine-cpp/include\""; + OTHER_LDFLAGS = "-DKANJI_MEMTRACE"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BB60170A1FD9289A009BD546 /* Build configuration list for PBXProject "spine_unit_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BB6017141FD9289A009BD546 /* Debug */, + BB6017151FD9289A009BD546 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BB6017161FD9289A009BD546 /* Build configuration list for PBXNativeTarget "spine_unit_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BB6017171FD9289A009BD546 /* Debug */, + BB6017181FD9289A009BD546 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BB6017071FD9289A009BD546 /* Project object */; +} diff --git a/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..af455f762 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + 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 new file mode 100644 index 000000000..5f09bba0d --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/SimpleTest.h @@ -0,0 +1,197 @@ +// +// SimpleTest.h +// spine_unit_test +// +// Created by Stephen Gowen on 11/9/17. +// Copyright © 2017 Noctis Games. All rights reserved. +// + +#ifndef SimpleTest_h +#define SimpleTest_h + +#include "SpineEventMonitor.h" + +#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 RAPTOR_JSON "testdata/raptor/raptor-pro.json" +#define RAPTOR_ATLAS "testdata/raptor/raptor.atlas" + +#define GOBLINS_JSON "testdata/goblins/goblins-pro.json" +#define GOBLINS_ATLAS "testdata/goblins/goblins.atlas" + +#define MAX_RUN_TIME 6000 // equal to about 100 seconds of execution + +namespace Spine +{ + class SimpleTest + { + public: + 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; + } + + typedef std::vector AnimList; + + static size_t enumerateAnimations(AnimList& outList, SkeletonData* skeletonData) + { + if (skeletonData) + { + for (int n = 0; n < skeletonData->getAnimations().size(); n++) + { + outList.push_back(skeletonData->getAnimations()[n]->getName()); + } + } + + return outList.size(); + } + + 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 + } + }; + + static void testRunner(const char* jsonName, const char* atlasName) + { + /////////////////////////////////////////////////////////////////////////// + // Global Animation Information + MyTextureLoader myTextureLoader; + Atlas* atlas = NEW(Atlas); + new (atlas) Atlas(atlasName, myTextureLoader); + assert(atlas != 0); + + SkeletonData* skeletonData = readSkeletonJsonData(jsonName, atlas); + assert(skeletonData != 0); + + AnimationStateData* stateData = NEW(AnimationStateData); + new (stateData) AnimationStateData(*skeletonData); + assert(stateData != 0); + stateData->setDefaultMix(0.2f); // force mixing + + /////////////////////////////////////////////////////////////////////////// + // Animation Instance + Skeleton* skeleton = NEW(Skeleton); + new (skeleton) Skeleton(*skeletonData); + assert(skeleton != 0); + + AnimationState* state = NEW(AnimationState); + new (state) AnimationState(*stateData); + assert(state != 0); + + /////////////////////////////////////////////////////////////////////////// + // Run animation + skeleton->setToSetupPose(); + SpineEventMonitor eventMonitor(state); + + AnimList anims; // Let's chain all the animations together as a test + size_t count = enumerateAnimations(anims, skeletonData); + if (count > 0) + { + state->setAnimation(0, anims[0].c_str(), false); + } + + for (size_t i = 1; i < count; ++i) + { + state->addAnimation(0, anims[i].c_str(), false, 0.0f); + } + + // Run Loop + 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); + } + + /////////////////////////////////////////////////////////////////////////// + // Dispose Instance + DESTROY(Skeleton, skeleton); + DESTROY(AnimationState, state); + + /////////////////////////////////////////////////////////////////////////// + // Dispose Global + DESTROY(AnimationStateData, stateData); + DESTROY(SkeletonData, skeletonData); + DESTROY(Atlas, atlas); + } + + static void spineboyTestCase() + { + testRunner(SPINEBOY_JSON, SPINEBOY_ATLAS); + } + + static void raptorTestCase() + { + testRunner(RAPTOR_JSON, RAPTOR_ATLAS); + } + + static void goblinsTestCase() + { + testRunner(GOBLINS_JSON, GOBLINS_ATLAS); + } + + static void test() + { + spineboyTestCase(); + raptorTestCase(); + goblinsTestCase(); + } + + private: + // ctor, copy ctor, and assignment should be private in a Singleton + SimpleTest(); + SimpleTest(const SimpleTest&); + SimpleTest& operator=(const SimpleTest&); + }; +} + +#endif /* SimpleTest_h */ 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 new file mode 100644 index 000000000..5ed9971da --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/spine_unit_test/spine_unit_test/main.cpp @@ -0,0 +1,119 @@ +// +// main.cpp +// spine_unit_test +// +// Created by Stephen Gowen on 12/7/17. +// Copyright © 2017 Noctis Games. All rights reserved. +// + +#ifdef WIN32 +#include +#else +#include +#endif // WIN32 + +#include +#include "KString.h" +#include + +#include "spine/Extension.h" + +#include "SimpleTest.h" +#include "MemoryTest.h" + +#include "KMemory.h" // last include + +using namespace Spine; + +class KanjiSpineExtension : public DefaultSpineExtension +{ +public: + static KanjiSpineExtension* getInstance(); + + virtual ~KanjiSpineExtension(); + + virtual void* spineAlloc(size_t size, const char* file, int line); + + virtual void* spineCalloc(size_t num, size_t size, const char* file, int line); + + virtual void* spineRealloc(void* ptr, size_t size, const char* file, int line); + + virtual void spineFree(void* mem); + +protected: + KanjiSpineExtension(); +}; + +KanjiSpineExtension* KanjiSpineExtension::getInstance() +{ + static KanjiSpineExtension ret; + return &ret; +} + +KanjiSpineExtension::~KanjiSpineExtension() +{ + // Empty +} + +void* KanjiSpineExtension::spineAlloc(size_t size, const char* file, int line) +{ + return _kanjimalloc(size); +} + +void* KanjiSpineExtension::spineCalloc(size_t num, size_t size, const char* file, int line) +{ + void* ptr = spineAlloc(num * size, file, line); + if (ptr) + { + memset(ptr, 0, num * size); + } + + return ptr; +} + +void* KanjiSpineExtension::spineRealloc(void* ptr, size_t size, const char* file, int line) +{ + return _kanjirealloc(ptr, size); +} + +void KanjiSpineExtension::spineFree(void* mem) +{ + _kanjifree(mem); +} + +KanjiSpineExtension::KanjiSpineExtension() : DefaultSpineExtension() +{ + // Empty +} + +double timeNow() +{ + timespec lTimeVal; + clock_gettime(CLOCK_MONOTONIC, &lTimeVal); + return lTimeVal.tv_sec + (lTimeVal.tv_nsec * 1.0e-9); +} + +int main(int argc, char* argv[]) +{ + SpineExtension::setInstance(KanjiSpineExtension::getInstance()); + + double startTime = timeNow(); + + /* Set working directory to current location for opening test data */ +#ifdef WIN32 + _chdir( GetFileDir(argv[0], false).c_str() ); +#else + chdir(GetFileDir(argv[0], false).c_str()); +#endif + + SimpleTest::test(); + MemoryTest::test(); + + // End Timing + double endTime = timeNow(); + double timeElapsed = (endTime - startTime); + printf("\n\n%i minutes and %i seconds of your life taken from you by these tests.\n", ((int)timeElapsed) / 60, ((int)timeElapsed) % 60); + printf("timeElapsed: %f \n", timeElapsed); + + return 0; +} diff --git a/spine-cpp/spine-cpp-unit-tests/teamcity/README.txt b/spine-cpp/spine-cpp-unit-tests/teamcity/README.txt new file mode 100755 index 000000000..04f7914b9 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/teamcity/README.txt @@ -0,0 +1,30 @@ +CppUnit listener for TeamCity +----------------------------- + +To report your tests result to TeamCity server +include teamcity_messages.* teamcity_cppunit.* +to your project and modify "main" function +as shown in example.cpp +(around JetBrains::underTeamcity and JetBrains::TeamcityProgressListener) + +Technical details +----------------- + +Reporting implemented by writing TeamCity service messages to stdout. + +See +http://www.jetbrains.net/confluence/display/TCD3/Build+Script+Interaction+with+TeamCity +for more details. + +Contact information +------------------- + +Mail to teamcity-feedback@jetbrains.com or see other options at + +http://www.jetbrains.com/support/teamcity + +License +------- + +Apache, version 2.0 +http://www.apache.org/licenses/LICENSE-2.0 diff --git a/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_cppunit.cpp b/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_cppunit.cpp new file mode 100755 index 000000000..86f163b9c --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_cppunit.cpp @@ -0,0 +1,82 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#include + +#include "teamcity_cppunit.h" + +using namespace std; + +namespace JetBrains { + +TeamcityProgressListener::TeamcityProgressListener() +{ + flowid = getFlowIdFromEnvironment(); +} + +TeamcityProgressListener::TeamcityProgressListener(const std::string& _flowid) +{ + flowid = _flowid; +} + +void TeamcityProgressListener::startTest(const std::string& test) { + messages.testStarted(test, flowid); +} + +static string sourceLine2string(const SourceLine &sline) { + stringstream ss; + + ss << sline.fileName << ":" << sline.lineNumber; + + return ss.str(); +} + +void TeamcityProgressListener::addFailure(const TestFailure &failure) +{ + + string details = failure.details; + + if (failure.sourceLine.isValid()) { + details.append(" at "); + details.append(sourceLine2string(failure.sourceLine)); + details.append("\n"); + } + + messages.testFailed( + failure.testName, + failure.description, + details, + flowid + ); +} + +void TeamcityProgressListener::endTest(const std::string& test) +{ + messages.testFinished(test, -1, flowid); +} + +void TeamcityProgressListener::startSuite(const std::string& test) +{ + messages.suiteStarted(test, flowid); +} + +void TeamcityProgressListener::endSuite(const std::string& test) +{ + messages.suiteFinished(test, flowid); +} + +} diff --git a/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_cppunit.h b/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_cppunit.h new file mode 100755 index 000000000..7541a5186 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_cppunit.h @@ -0,0 +1,83 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#pragma once + +#include + +#include "teamcity_messages.h" + +namespace JetBrains { + + class SourceLine + { + public: + SourceLine():lineNumber(-1){} + SourceLine(const std::string& theFile, int theLineNum):fileName(theFile),lineNumber(theLineNum){} + ~SourceLine(){} + + std::string fileName; + int lineNumber; + bool isValid() const {return (!fileName.empty() && lineNumber > -1);} + }; + + class TestFailure + { + public: + std::string details; + SourceLine sourceLine; + std::string testName; + std::string description; + public: + TestFailure(){} + ~TestFailure(){} + + TestFailure(const std::string& theTestName, const std::string& theDetails, SourceLine theSourcelLine, const std::string& theDescription) + { + testName = theTestName; + details = theDetails; + sourceLine = theSourcelLine; + description = theDescription; + } + }; + + class TeamcityProgressListener + { + public: + TeamcityMessages messages; + public: + TeamcityProgressListener(const std::string& _flowid); + TeamcityProgressListener(); + ~TeamcityProgressListener(){} + + void startTest(const std::string& test); + void addFailure(const TestFailure &failure); + void endTest(const std::string& test); + void startSuite(const std::string& test); + void endSuite(const std::string& test); + + private: + std::string flowid; + + // Prevents the use of the copy constructor. + TeamcityProgressListener(const TeamcityProgressListener ©); + + // Prevents the use of the copy operator. + void operator =(const TeamcityProgressListener ©); + }; + +} \ No newline at end of file diff --git a/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_messages.cpp b/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_messages.cpp new file mode 100755 index 000000000..6f88d1ee9 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_messages.cpp @@ -0,0 +1,174 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#include +#include + +#include "teamcity_messages.h" + +using namespace std; + +namespace JetBrains { + +std::string getFlowIdFromEnvironment() { + const char *flowId = getenv("TEAMCITY_PROCESS_FLOW_ID"); + return flowId == NULL ? "" : flowId; +} + +bool underTeamcity() { + return getenv("TEAMCITY_PROJECT_NAME") != NULL; +} + +TeamcityMessages::TeamcityMessages() +: m_out(&cout) +{} + +void TeamcityMessages::setOutput(ostream &out) { + m_out = &out; +} + +string TeamcityMessages::escape(string s) { + string result; + + for (size_t i = 0; i < s.length(); i++) { + char c = s[i]; + + switch (c) { + case '\n': result.append("|n"); break; + case '\r': result.append("|r"); break; + case '\'': result.append("|'"); break; + case '|': result.append("||"); break; + case ']': result.append("|]"); break; + default: result.append(&c, 1); + } + } + + return result; +} + +void TeamcityMessages::openMsg(const string &name) { + // endl for http://jetbrains.net/tracker/issue/TW-4412 + *m_out << endl << "##teamcity[" << name; +} + +void TeamcityMessages::closeMsg() { + *m_out << "]"; + // endl for http://jetbrains.net/tracker/issue/TW-4412 + *m_out << endl; + m_out->flush(); +} + +void TeamcityMessages::writeProperty(string name, string value) { + *m_out << " " << name << "='" << escape(value) << "'"; +} + +void TeamcityMessages::suiteStarted(string name, string flowid) { + openMsg("testSuiteStarted"); + writeProperty("name", name); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::suiteFinished(string name, string flowid) { + openMsg("testSuiteFinished"); + writeProperty("name", name); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::testStarted(string name, string flowid) { + openMsg("testStarted"); + writeProperty("name", name); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::testFinished(string name, int durationMs, string flowid) { + openMsg("testFinished"); + + writeProperty("name", name); + + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + if(durationMs >= 0) { + stringstream out; + out << durationMs; + writeProperty("duration", out.str()); + } + + closeMsg(); +} + +void TeamcityMessages::testFailed(string name, string message, string details, string flowid) { + openMsg("testFailed"); + writeProperty("name", name); + writeProperty("message", message); + writeProperty("details", details); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::testIgnored(std::string name, std::string message, string flowid) { + openMsg("testIgnored"); + writeProperty("name", name); + writeProperty("message", message); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::messageError(const std::string& text) +{ + openMsg("message"); + writeProperty("text", text); + writeProperty("status", "ERROR"); + closeMsg(); +} + +void TeamcityMessages::messageWarning(const std::string& text) +{ + openMsg("message"); + writeProperty("text", text); + writeProperty("status", "WARNING"); + closeMsg(); +} + +void TeamcityMessages::messageNormal(const std::string& text) +{ + openMsg("message"); + writeProperty("text", text); + writeProperty("status", "NORMAL"); + closeMsg(); +} + +} diff --git a/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_messages.h b/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_messages.h new file mode 100755 index 000000000..36ad80797 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/teamcity/teamcity_messages.h @@ -0,0 +1,59 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#ifndef H_TEAMCITY_MESSAGES +#define H_TEAMCITY_MESSAGES + +#include +#include + +namespace JetBrains { + +std::string getFlowIdFromEnvironment(); +bool underTeamcity(); + +class TeamcityMessages { + std::ostream *m_out; + +protected: + std::string escape(std::string s); + + void openMsg(const std::string &name); + void writeProperty(std::string name, std::string value); + void closeMsg(); + +public: + TeamcityMessages(); + + void setOutput(std::ostream &); + + void suiteStarted(std::string name, std::string flowid = ""); + void suiteFinished(std::string name, std::string flowid = ""); + + void testStarted(std::string name, std::string flowid = ""); + void testFailed(std::string name, std::string message, std::string details, std::string flowid = ""); + void testIgnored(std::string name, std::string message, std::string flowid = ""); + void testFinished(std::string name, int durationMs = -1, std::string flowid = ""); + + void messageError(const std::string& text); + void messageWarning(const std::string& text); + void messageNormal(const std::string& text); +}; + +} + +#endif /* H_TEAMCITY_MESSAGES */ diff --git a/spine-cpp/spine-cpp-unit-tests/tests/CPP_InterfaceTestFixture.cpp b/spine-cpp/spine-cpp-unit-tests/tests/CPP_InterfaceTestFixture.cpp new file mode 100755 index 000000000..dde75c363 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/CPP_InterfaceTestFixture.cpp @@ -0,0 +1,48 @@ +////////////////////////////////////////////////////////////////////// +// filename: C_InterfaceTestFixture.cpp +// +// notes: There is no C++ interface! +// +///////////////////////////////////////////////////////////////////// + +#include "CPP_InterfaceTestFixture.h" + +CPP_InterfaceTestFixture::~CPP_InterfaceTestFixture() +{ + finalize(); +} + +void CPP_InterfaceTestFixture::initialize() +{ + // on a Per- Fixture Basis, before Test execution +} + +void CPP_InterfaceTestFixture::finalize() +{ + // on a Per- Fixture Basis, after all tests pass/fail +} + +void CPP_InterfaceTestFixture::setUp() +{ + // Setup on Per-Test Basis +} + +void CPP_InterfaceTestFixture::tearDown() +{ + // Tear Down on Per-Test Basis +} + +void CPP_InterfaceTestFixture::spineboyTestCase() +{ + // There is no C++ interface. +} + +void CPP_InterfaceTestFixture::raptorTestCase() +{ + // There is no C++ interface. +} + +void CPP_InterfaceTestFixture::goblinsTestCase() +{ + // No c++ interface +} diff --git a/spine-cpp/spine-cpp-unit-tests/tests/CPP_InterfaceTestFixture.h b/spine-cpp/spine-cpp-unit-tests/tests/CPP_InterfaceTestFixture.h new file mode 100755 index 000000000..8dc5fed36 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/CPP_InterfaceTestFixture.h @@ -0,0 +1,47 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// filename: C_InterfaceTestFixture.h +// +// purpose: Run example animations for regression testing +// on "C++" interface to make sure modifications to "C" +// interface doesn't cause memory leaks or regression +// errors. +///////////////////////////////////////////////////////////////////// + +#include "MiniCppUnit.hxx" +#include "TestOptions.h" + +class CPP_InterfaceTestFixture : public TestFixture < CPP_InterfaceTestFixture > +{ +public: + TEST_FIXTURE(CPP_InterfaceTestFixture){ + TEST_CASE(spineboyTestCase); + TEST_CASE(raptorTestCase); + TEST_CASE(goblinsTestCase); + + initialize(); + } + + virtual ~CPP_InterfaceTestFixture(); + + ////////////////////////////////////////////////////////////////////////// + // Test Cases + ////////////////////////////////////////////////////////////////////////// +public: + void spineboyTestCase(); + void raptorTestCase(); + void goblinsTestCase(); + + ////////////////////////////////////////////////////////////////////////// + // test fixture setup + ////////////////////////////////////////////////////////////////////////// + void initialize(); + void finalize(); +public: + virtual void setUp(); + virtual void tearDown(); + +}; +#if defined(gForceAllTests) || defined(gCPPInterfaceTestFixture) +REGISTER_FIXTURE(CPP_InterfaceTestFixture); +#endif diff --git a/spine-cpp/spine-cpp-unit-tests/tests/C_InterfaceTestFixture.cpp b/spine-cpp/spine-cpp-unit-tests/tests/C_InterfaceTestFixture.cpp new file mode 100755 index 000000000..072c8531d --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/C_InterfaceTestFixture.cpp @@ -0,0 +1,175 @@ +#include "C_InterfaceTestFixture.h" +#include "SpineEventMonitor.h" + +#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 RAPTOR_JSON "testdata/raptor/raptor-pro.json" +#define RAPTOR_ATLAS "testdata/raptor/raptor.atlas" + +#define GOBLINS_JSON "testdata/goblins/goblins-pro.json" +#define GOBLINS_ATLAS "testdata/goblins/goblins.atlas" + +#define MAX_RUN_TIME 6000 // equal to about 100 seconds of execution + +void C_InterfaceTestFixture::setUp() +{ +} + +void C_InterfaceTestFixture::tearDown() +{ +} + +static Spine::SkeletonData* readSkeletonJsonData(const char* filename, Atlas* atlas) +{ + using namespace Spine; + + 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; +} + +typedef std::vector AnimList; + +static size_t enumerateAnimations(AnimList& outList, SkeletonData* skeletonData) +{ + if (skeletonData) + { + for (int n = 0; n < skeletonData->getAnimations().size(); n++) + { + outList.push_back(skeletonData->getAnimations()[n]->getName()); + } + } + + return outList.size(); +} + +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 + } +}; + +static void testRunner(const char* jsonName, const char* atlasName) +{ + /////////////////////////////////////////////////////////////////////////// + // Global Animation Information + MyTextureLoader myTextureLoader; + Atlas* atlas = NEW(Atlas); + new (atlas) Atlas(atlasName, myTextureLoader); + ASSERT(atlas != 0); + + SkeletonData* skeletonData = readSkeletonJsonData(jsonName, atlas); + ASSERT(skeletonData != 0); + + AnimationStateData* stateData = NEW(AnimationStateData); + new (stateData) AnimationStateData(skeletonData); + ASSERT(stateData != 0); + stateData->setDefaultMix(0.2f); // force mixing + + /////////////////////////////////////////////////////////////////////////// + // Animation Instance + Skeleton* skeleton = NEW(Skeleton); + new (skeleton) Skeleton(skeletonData); + ASSERT(skeleton != 0); + + AnimationState* state = NEW(AnimationState); + new (state) AnimationState(stateData); + ASSERT(state != 0); + + /////////////////////////////////////////////////////////////////////////// + // Run animation + skeleton->setToSetupPose(); + SpineEventMonitor eventMonitor(state); + + AnimList anims; // Let's chain all the animations together as a test + size_t count = enumerateAnimations(anims, skeletonData); + if (count > 0) + { + state->setAnimation(0, anims[0].c_str(), false); + } + + for (size_t i = 1; i < count; ++i) + { + state->addAnimation(0, anims[i].c_str(), false, 0.0f); + } + + // Run Loop + 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); + } + + /////////////////////////////////////////////////////////////////////////// + // Dispose Instance + DESTROY(Skeleton, skeleton); + DESTROY(AnimationState, state); + + /////////////////////////////////////////////////////////////////////////// + // Dispose Global + DESTROY(AnimationStateData, stateData); + DESTROY(SkeletonData, skeletonData); + DESTROY(Atlas, atlas); +} + +void C_InterfaceTestFixture::spineboyTestCase() +{ + testRunner(SPINEBOY_JSON, SPINEBOY_ATLAS); +} + +void C_InterfaceTestFixture::raptorTestCase() +{ + testRunner(RAPTOR_JSON, RAPTOR_ATLAS); +} + +void C_InterfaceTestFixture::goblinsTestCase() +{ + testRunner(GOBLINS_JSON, GOBLINS_ATLAS); +} diff --git a/spine-cpp/spine-cpp-unit-tests/tests/C_InterfaceTestFixture.h b/spine-cpp/spine-cpp-unit-tests/tests/C_InterfaceTestFixture.h new file mode 100755 index 000000000..2f30d2bea --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/C_InterfaceTestFixture.h @@ -0,0 +1,33 @@ +#pragma once +////////////////////////////////////////////////////////////////////// +// filename: C_InterfaceTestFixture.h +// +// purpose: Run example animations for regression testing +// on "C" interface +///////////////////////////////////////////////////////////////////// + +#include "TestOptions.h" +#include "MiniCppUnit.hxx" + +class C_InterfaceTestFixture : public TestFixture +{ +public: + TEST_FIXTURE(C_InterfaceTestFixture) + { + // enable/disable individual tests here + TEST_CASE(spineboyTestCase); + TEST_CASE(raptorTestCase); + TEST_CASE(goblinsTestCase); + } + +public: + virtual void setUp(); + virtual void tearDown(); + + void spineboyTestCase(); + void raptorTestCase(); + void goblinsTestCase(); +}; +#if defined(gForceAllTests) || defined(gCInterfaceTestFixture) +REGISTER_FIXTURE(C_InterfaceTestFixture); +#endif \ No newline at end of file diff --git a/spine-cpp/spine-cpp-unit-tests/tests/EmptyTestFixture.cpp b/spine-cpp/spine-cpp-unit-tests/tests/EmptyTestFixture.cpp new file mode 100755 index 000000000..d2260b7d5 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/EmptyTestFixture.cpp @@ -0,0 +1,25 @@ +#include "EmptyTestFixture.h" + +#include "KMemory.h" // Last include + + +void EmptyTestFixture::setUp() +{ +} + +void EmptyTestFixture::tearDown() +{ +} + +void EmptyTestFixture::emptyTestCase_1() +{ + // char* pLeak = new char[256]; // test leak detector +} + +void EmptyTestFixture::emptyTestCase_2() +{ +} + +void EmptyTestFixture::emptyTestCase_3() +{ +} diff --git a/spine-cpp/spine-cpp-unit-tests/tests/EmptyTestFixture.h b/spine-cpp/spine-cpp-unit-tests/tests/EmptyTestFixture.h new file mode 100755 index 000000000..c11cfd5f2 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/EmptyTestFixture.h @@ -0,0 +1,26 @@ +#pragma once +#include "TestOptions.h" +#include "MiniCppUnit.hxx" + +class EmptyTestFixture : public TestFixture +{ +public: + TEST_FIXTURE(EmptyTestFixture) + { + // enable/disable individual tests here + TEST_CASE(emptyTestCase_1); + TEST_CASE(emptyTestCase_2); + TEST_CASE(emptyTestCase_3); + } + +public: + virtual void setUp(); + virtual void tearDown(); + + void emptyTestCase_1(); + void emptyTestCase_2(); + void emptyTestCase_3(); +}; +#if defined(gForceAllTests) || defined(gEmptyTestFixture) +REGISTER_FIXTURE(EmptyTestFixture); +#endif \ No newline at end of file diff --git a/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.cpp b/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.cpp new file mode 100755 index 000000000..d06c196e6 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.cpp @@ -0,0 +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); +} + + + diff --git a/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.h b/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.h new file mode 100755 index 000000000..f45c35c86 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/MemoryTestFixture.h @@ -0,0 +1,50 @@ +////////////////////////////////////////////////////////////////////// +// filename: MemoryTestFixture.h +// +// purpose: Reproduce Memory Error/Leak Bugs to help debug +// and for regression testing +///////////////////////////////////////////////////////////////////// + +#pragma once +#include "MiniCppUnit.hxx" +#include "TestOptions.h" + +class MemoryTestFixture : public TestFixture < MemoryTestFixture > +{ +public: + TEST_FIXTURE(MemoryTestFixture){ + + // Comment out here to disable individual test cases + TEST_CASE(reproduceIssue_776); + TEST_CASE(reproduceIssue_777); + TEST_CASE(reproduceIssue_Loop); + TEST_CASE(triangulator); + TEST_CASE(skeletonClipper); + + initialize(); + } + + virtual ~MemoryTestFixture(); + + ////////////////////////////////////////////////////////////////////////// + // Test Cases + ////////////////////////////////////////////////////////////////////////// +public: + void reproduceIssue_776(); + void reproduceIssue_777(); + void reproduceIssue_Loop(); // http://esotericsoftware.com/forum/spine-c-3-5-animation-jerking-7451 + void triangulator(); + void skeletonClipper(); + + ////////////////////////////////////////////////////////////////////////// + // test fixture setup + ////////////////////////////////////////////////////////////////////////// + void initialize(); + void finalize(); +public: + virtual void setUp(); + virtual void tearDown(); +}; +#if defined(gForceAllTests) || defined(gMemoryTestFixture) +REGISTER_FIXTURE(MemoryTestFixture); +#endif \ No newline at end of file diff --git a/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.cpp b/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.cpp new file mode 100755 index 000000000..9506e354c --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.cpp @@ -0,0 +1,181 @@ +#include "SpineEventMonitor.h" + +#include "KString.h" + +#include +#include + +#include "KMemory.h" // Last include + +using namespace Spine; + +SpineEventMonitor::SpineEventMonitor(AnimationState* _pAnimationState /*= nullptr*/) +{ + bLogging = false; + RegisterListener(_pAnimationState); +} + +SpineEventMonitor::~SpineEventMonitor() +{ + pAnimState = 0; +} + +void SpineEventMonitor::RegisterListener(AnimationState * _pAnimationState) +{ + if (_pAnimationState) + { + _pAnimationState->setRendererObject(this); + _pAnimationState->setOnAnimationEventFunc(&SpineEventMonitor::spineAnimStateHandler); + } + pAnimState = _pAnimationState; +} + +bool SpineEventMonitor::isAnimationPlaying() +{ + if (pAnimState) + { + return pAnimState->getCurrent(0) != NULL; + } + return false; +} + +void SpineEventMonitor::spineAnimStateHandler(AnimationState* state, EventType type, TrackEntry* entry, Event* event) +{ + if (state && state->getRendererObject()) + { + SpineEventMonitor* pEventMonitor = (SpineEventMonitor*)state->getRendererObject(); + pEventMonitor->OnSpineAnimationStateEvent(state, type, entry, event); + } +} + +void SpineEventMonitor::OnSpineAnimationStateEvent(AnimationState* state, EventType type, TrackEntry* entry, Event* event) +{ + const char* eventName = 0; + if (state == pAnimState) + { + // only monitor ours + switch(type) + { + case EventType_Start: eventName = "EventType_Start"; break; + case EventType_Interrupt: eventName = "EventType_Interrupt"; break; + case EventType_End: eventName = "EventType_End"; break; + case EventType_Complete: eventName = "EventType_Complete"; break; + case EventType_Dispose: eventName = "EventType_Dispose"; break; + case EventType_Event: eventName = "EventType_Event"; break; + default: + break; + } + + if (bLogging && eventName && entry && entry->getAnimation()) + KOutputDebug(DEBUGLVL, "[%s : '%s']\n", eventName, entry->getAnimation()->getName().c_str());//*/ + } +} + +InterruptMonitor::InterruptMonitor(AnimationState * _pAnimationState): + SpineEventMonitor(_pAnimationState) +{ + bForceInterrupt = false; + mEventStackCursor = 0; // cursor used to track events +} + +bool InterruptMonitor::isAnimationPlaying() +{ + return !bForceInterrupt && SpineEventMonitor::isAnimationPlaying(); +} + +// Stops the animation on any occurance of the spEventType +InterruptMonitor& InterruptMonitor::AddInterruptEvent(int theEventType) +{ + InterruptEvent ev; + ev.mEventType = theEventType; + mEventStack.push_back(ev); + return *this; +} + +// Stops the animation when the [spEventType : 'animationName'] occurs +InterruptMonitor& InterruptMonitor::AddInterruptEvent(int theEventType, const std::string & theAnimationName) +{ + InterruptEvent ev; + ev.mEventType = theEventType; + ev.mAnimName = theAnimationName; + mEventStack.push_back(ev); + return *this; +} + +// stops the first encounter of spEventType on the specified TrackEntry +InterruptMonitor& InterruptMonitor::AddInterruptEvent(int theEventType, TrackEntry * theTrackEntry) +{ + InterruptEvent ev; + ev.mEventType = theEventType; + ev.mTrackEntry = theTrackEntry; + mEventStack.push_back(ev); + return *this; +} + +// Stops on the first SP_ANIMATION_EVENT with the string payload of 'theEventTriggerName' +InterruptMonitor& InterruptMonitor::AddInterruptEventTrigger(const std::string & theEventTriggerName) +{ + InterruptEvent ev; + ev.mEventType = EventType_Event; + ev.mEventName = theEventTriggerName; + mEventStack.push_back(ev); + return *this; +} + +void InterruptMonitor::OnSpineAnimationStateEvent(AnimationState * state, EventType type, TrackEntry * trackEntry, Event * event) +{ + SpineEventMonitor::OnSpineAnimationStateEvent(state, type, trackEntry, event); + + if (mEventStackCursor < mEventStack.size()) + { + if (mEventStack[mEventStackCursor].matches(state, type, trackEntry, event)) + { + ++mEventStackCursor; + } + + if (mEventStackCursor >= mEventStack.size()) + { + bForceInterrupt = true; + OnMatchingComplete(); + } + } +} + +inline bool InterruptMonitor::InterruptEvent::matches(AnimationState * state, EventType type, TrackEntry * trackEntry, Event * 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 + if (mTrackEntry != 0) + { + return mTrackEntry == trackEntry; + } + + // looking for Animation Track by name + if (!mAnimName.empty()) + { + if (trackEntry && trackEntry->getAnimation()) + { + if (CompareNoCase(trackEntry->getAnimation()->getName(), mAnimName) == 0) + { + return true; + } + } + return false; + } + + // looking for Event String Text + if (!mEventName.empty()) + { + if (event) + { + return (CompareNoCase(event->getStringValue(), mEventName) == 0); + } + return false; + } + + return true; // waiting for ANY spEventType that matches + } + return false; +} diff --git a/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.h b/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.h new file mode 100755 index 000000000..4590a1e6e --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/SpineEventMonitor.h @@ -0,0 +1,121 @@ +////////////////////////////////////////////////////////////////////// +// filename: SpineEventMonitor.h +// +// purpose: Monitor spAnimationState Events +///////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include + +#include + +using namespace Spine; + +////////////////////////////////////////////////////////////////////// +// class: SpineEventMonitor +// +// purpose: Monitor spAnimationState Events and report when there +// are no more TrackEntry(s) waiting to play on track 0; +// +// Also allows for debug printing of Events to console. +///////////////////////////////////////////////////////////////////// +class SpineEventMonitor +{ +public: + SpineEventMonitor(AnimationState* _pAnimationState = 0); + virtual ~SpineEventMonitor(); + + void RegisterListener(AnimationState* _pAnimationState); + + void SetDebugLogging(bool val) { bLogging = val; } + bool GetDebugLogging() { return bLogging; } + + virtual bool isAnimationPlaying(); + +protected: + static void spineAnimStateHandler(AnimationState* state, EventType type, TrackEntry* entry, Event* event); + virtual void OnSpineAnimationStateEvent(AnimationState* state, EventType type, TrackEntry* entry, Event* event); + +protected: + AnimationState *pAnimState; + bool bLogging; +}; + +////////////////////////////////////////////////////////////////////// +// class: InterruptMonitor +// +// purpose: Allows a programmer to interrupt/stop the updating +// of an animation based on a specific sequence of +// events generated by the animation. +///////////////////////////////////////////////////////////////////// +class InterruptMonitor : public SpineEventMonitor +{ +private: + struct InterruptEvent + { + InterruptEvent() { + mEventType = -1; // invalid + mTrackEntry = 0; + } + + bool matches(AnimationState* state, EventType type, TrackEntry* trackEntry, Event* event); + + std::string mAnimName; + int mEventType; + TrackEntry* mTrackEntry; + std::string mEventName; + }; + typedef std::vector InterruptEventStack; + + +public: + InterruptMonitor(AnimationState* _pAnimationState = 0); + ~InterruptMonitor() {} + + virtual bool isAnimationPlaying() override; + +public: + InterruptMonitor& AddInterruptEvent(int theEventType); + InterruptMonitor& AddInterruptEvent(int theEventType, const std::string& theAnimationName); + InterruptMonitor& AddInterruptEvent(int theEventType, TrackEntry* theTrackEntry); + InterruptMonitor& AddInterruptEventTrigger(const std::string& theEventTriggerName); + +protected: + virtual void OnSpineAnimationStateEvent(AnimationState* state, EventType type, TrackEntry* trackEntry, Event* event) override; + virtual void OnMatchingComplete() {} + +protected: + bool bForceInterrupt; + InterruptEventStack mEventStack; // must match these events in this order + size_t mEventStackCursor; +}; + +/* + +EXAMPLE +======= + +SpineEventMonitor eventMonitor(state); +eventMonitor.SetDebugLogging(true); + +while(eventMonitor.isAnimationPlaying()){ + // update... +} + + + +EXAMPLE +======= + +InterruptMonitor eventMonitor(state); +eventMonitor.SetDebugLogging(true); + +// Interrupt the animation on this specific sequence of spEventType(s) +eventMonitor + .AddInterruptEvent(SP_ANIMATION_INTERRUPT, "jump") // First, wait for INTERRUPT signal on the 'jump' animation TrackEntry + .AddInterruptEvent(SP_ANIMATION_START); // Then, stop on any following START signal + + +*/ diff --git a/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/CPP_InterfaceTestFixture.cpp b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/CPP_InterfaceTestFixture.cpp new file mode 100755 index 000000000..9c2a964a9 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/CPP_InterfaceTestFixture.cpp @@ -0,0 +1,26 @@ +#include "CPP_InterfaceTestFixture.h" + +CPP_InterfaceTestFixture::~CPP_InterfaceTestFixture() +{ + finalize(); +} + +void CPP_InterfaceTestFixture::initialize() +{ + // on a Per- Fixture Basis, before Test execution +} + +void CPP_InterfaceTestFixture::finalize() +{ + // on a Per- Fixture Basis, after all tests pass/fail +} + +void CPP_InterfaceTestFixture::setUp() +{ + // Setup on Per-Test Basis +} + +void CPP_InterfaceTestFixture::tearDown() +{ + // Tear Down on Per-Test Basis +} diff --git a/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/CPP_InterfaceTestFixture.h b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/CPP_InterfaceTestFixture.h new file mode 100755 index 000000000..5fcb99c79 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/CPP_InterfaceTestFixture.h @@ -0,0 +1,30 @@ +#pragma once +#include "MiniCppUnit.hxx" + +class CPP_InterfaceTestFixture : public TestFixture < CPP_InterfaceTestFixture > +{ +public: + TEST_FIXTURE(CPP_InterfaceTestFixture){ + //TEST_CASE(parseJSON); + + initialize(); + } + + virtual ~CPP_InterfaceTestFixture(); + + ////////////////////////////////////////////////////////////////////////// + // Test Cases + ////////////////////////////////////////////////////////////////////////// +public: + // void parseJSON(); + + ////////////////////////////////////////////////////////////////////////// + // test fixture setup + ////////////////////////////////////////////////////////////////////////// + void initialize(); + void finalize(); +public: + virtual void setUp(); + virtual void tearDown(); +}; +REGISTER_FIXTURE(CPP_InterfaceTestFixture); \ No newline at end of file diff --git a/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/_TestFixture.cpp b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/_TestFixture.cpp new file mode 100755 index 000000000..67780f31a --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/_TestFixture.cpp @@ -0,0 +1,26 @@ +#include "[[FIXTURE_TYPE]].h" + +[[FIXTURE_TYPE]]::~[[FIXTURE_TYPE]]() +{ + finalize(); +} + +void [[FIXTURE_TYPE]]::initialize() +{ + // on a Per- Fixture Basis, before Test execution +} + +void [[FIXTURE_TYPE]]::finalize() +{ + // on a Per- Fixture Basis, after all tests pass/fail +} + +void [[FIXTURE_TYPE]]::setUp() +{ + // Setup on Per-Test Basis +} + +void [[FIXTURE_TYPE]]::tearDown() +{ + // Tear Down on Per-Test Basis +} diff --git a/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/_TestFixture.h b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/_TestFixture.h new file mode 100755 index 000000000..3103cda85 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/_TestFixture.h @@ -0,0 +1,30 @@ +#pragma once +#include "MiniCppUnit.hxx" + +class [[FIXTURE_TYPE]] : public TestFixture < [[FIXTURE_TYPE]] > +{ +public: + TEST_FIXTURE([[FIXTURE_TYPE]]){ + //TEST_CASE(parseJSON); + + initialize(); + } + + virtual ~[[FIXTURE_TYPE]](); + + ////////////////////////////////////////////////////////////////////////// + // Test Cases + ////////////////////////////////////////////////////////////////////////// +public: + // void parseJSON(); + + ////////////////////////////////////////////////////////////////////////// + // test fixture setup + ////////////////////////////////////////////////////////////////////////// + void initialize(); + void finalize(); +public: + virtual void setUp(); + virtual void tearDown(); +}; +REGISTER_FIXTURE([[FIXTURE_TYPE]]); \ No newline at end of file diff --git a/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/fnr.exe b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/fnr.exe new file mode 100755 index 000000000..548fc5d91 Binary files /dev/null and b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/fnr.exe differ diff --git a/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/makeFixture.bat b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/makeFixture.bat new file mode 100755 index 000000000..634154a29 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/TestFixtureGenerator/makeFixture.bat @@ -0,0 +1,17 @@ +@echo off +if "%1"=="" goto blank + +copy "_TestFixture.cpp" "%1TestFixture.cpp" +copy "_TestFixture.h" "%1TestFixture.h" + + +fnr --cl --find "[[FIXTURE_TYPE]]" --replace "%1TestFixture" --fileMask "%1TestFixture.cpp" --dir %cd% +fnr --cl --find "[[FIXTURE_TYPE]]" --replace "%1TestFixture" --fileMask "%1TestFixture.h" --dir %cd% + +goto done + +:blank +echo Usage: +echo %~n0 FixtureTypeName + +:done \ No newline at end of file diff --git a/spine-cpp/spine-cpp-unit-tests/tests/TestOptions.h b/spine-cpp/spine-cpp-unit-tests/tests/TestOptions.h new file mode 100755 index 000000000..7f00ffbb7 --- /dev/null +++ b/spine-cpp/spine-cpp-unit-tests/tests/TestOptions.h @@ -0,0 +1,32 @@ +#pragma once + +////////////////////////////////////////////////////////////////////////// +// Force all Tests to 'ON.' Use this for final 'Regression' Testing. + +//#define gForceAllTests + +//#define TURN_ON_ALL_TESTS // Comment this line out to switch to fast testing only + +#ifdef TURN_ON_ALL_TESTS +////////////////////////////////////////////////////////////////////////// +// All tests are ON by default, but you can turn off individual tests. + +#define gEmptyTestFixture +#define gCInterfaceTestFixture +#define gCPPInterfaceTestFixture +#define gMemoryTestFixture + + +#else + +////////////////////////////////////////////////////////////////////////// +// Slow Tests are disabled by default. Use this section to turn on +// Individual tests. +#define gEmptyTestFixture // Fast + +#define gCInterfaceTestFixture // slow +#define gCPPInterfaceTestFixture // fast + +#define gMemoryTestFixture // medium + +#endif \ No newline at end of file diff --git a/spine-cpp/spine-cpp/include/spine/Animation.h b/spine-cpp/spine-cpp/include/spine/Animation.h new file mode 100644 index 000000000..6713fd6b2 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Animation.h @@ -0,0 +1,100 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Animation_h +#define Spine_Animation_h + +#include +#include +#include + +#include + +namespace Spine { + class Timeline; + class Skeleton; + class Event; + + class Animation { + friend class AnimationState; + friend class TrackEntry; + friend class AnimationStateData; + + friend class AttachmentTimeline; + friend class ColorTimeline; + friend class DeformTimeline; + friend class DrawOrderTimeline; + friend class EventTimeline; + friend class IkConstraintTimeline; + friend class PathConstraintMixTimeline; + friend class PathConstraintPositionTimeline; + friend class PathConstraintSpacingTimeline; + friend class RotateTimeline; + friend class ScaleTimeline; + friend class ShearTimeline; + friend class TransformConstraintTimeline; + friend class TranslateTimeline; + friend class TwoColorTimeline; + + public: + Animation(std::string name, Vector& timelines, float duration); + + ~Animation(); + + /// Applies all the animation's timelines to the specified skeleton. + /// See also Timeline::apply(Skeleton&, float, float, Vector, float, MixPose, MixDirection) + void apply(Skeleton& skeleton, float lastTime, float time, bool loop, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + std::string getName(); + + Vector getTimelines(); + + void setTimelines(Vector inValue); + + float getDuration(); + + void setDuration(float inValue); + + private: + Vector _timelines; + float _duration; + std::string _name; + + /// @param target After the first and before the last entry. + static int binarySearch(Vector& values, float target, int step); + + /// @param target After the first and before the last entry. + static int binarySearch(Vector& values, float target); + + static int linearSearch(Vector& values, float target, int step); + }; +} + +#endif /* Spine_Animation_h */ diff --git a/spine-cpp/spine-cpp/include/spine/AnimationState.h b/spine-cpp/spine-cpp/include/spine/AnimationState.h new file mode 100644 index 000000000..404193400 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/AnimationState.h @@ -0,0 +1,422 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_AnimationState_h +#define Spine_AnimationState_h + +#include +#include +#include + +namespace Spine { + enum EventType { + EventType_Start, + EventType_Interrupt, + EventType_End, + EventType_Complete, + EventType_Dispose, + EventType_Event + }; + + class AnimationState; + class TrackEntry; + + class Animation; + class Event; + class AnimationStateData; + class Skeleton; + class RotateTimeline; + + typedef void (*OnAnimationEventFunc) (AnimationState* state, EventType type, TrackEntry* entry, Event* event); + + /// State for the playback of an animation + class TrackEntry { + friend class EventQueue; + friend class AnimationState; + + public: + TrackEntry(); + + /// The index of the track where this entry is either current or queued. + int getTrackIndex(); + + /// The animation to apply for this track entry. + Animation* getAnimation(); + + /// + /// If true, the animation will repeat. If false, it will not, instead its last frame is applied if played beyond its duration. + bool getLoop(); + void setLoop(bool inValue); + + /// + /// Seconds to postpone playing the animation. When a track entry is the current track entry, delay postpones incrementing + /// the track time. When a track entry is queued, delay is the time from the start of the previous animation to when the + /// track entry will become the current track entry. + float getDelay(); + void setDelay(float inValue); + + /// + /// Current time in seconds this track entry has been the current track entry. The track time determines + /// TrackEntry.AnimationTime. The track time can be set to start the animation at a time other than 0, without affecting looping. + float getTrackTime(); + void setTrackTime(float inValue); + + /// + /// The track time in seconds when this animation will be removed from the track. Defaults to the animation duration for + /// non-looping animations and to int.MaxValue for looping animations. If the track end time is reached and no + /// other animations are queued for playback, and mixing from any previous animations is complete, properties keyed by the animation, + /// are set to the setup pose and the track is cleared. + /// + /// It may be desired to use AnimationState.addEmptyAnimation(int, float, float) to mix the properties back to the + /// setup pose over time, rather than have it happen instantly. + /// + float getTrackEnd(); + void setTrackEnd(float inValue); + + /// + /// Seconds when this animation starts, both initially and after looping. Defaults to 0. + /// + /// When changing the animation start time, it often makes sense to set TrackEntry.AnimationLast to the same value to + /// prevent timeline keys before the start time from triggering. + /// + float getAnimationStart(); + void setAnimationStart(float inValue); + + /// + /// Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will + /// loop back to TrackEntry.AnimationStart at this time. Defaults to the animation duration. + float getAnimationEnd(); + void setAnimationEnd(float inValue); + + /// + /// The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this + /// animation is applied, event timelines will fire all events between the animation last time (exclusive) and animation time + /// (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation is applied. + float getAnimationLast(); + void setAnimationLast(float inValue); + + /// + /// Uses TrackEntry.TrackTime to compute the animation time between TrackEntry.AnimationStart. and + /// TrackEntry.AnimationEnd. When the track time is 0, the animation time is equal to the animation start time. + /// + float getAnimationTime(); + + /// + /// Multiplier for the delta time when the animation state is updated, causing time for this animation to play slower or + /// faster. Defaults to 1. + /// + float getTimeScale(); + void setTimeScale(float inValue); + + /// + /// Values less than 1 mix this animation with the last skeleton pose. Defaults to 1, which overwrites the last skeleton pose with + /// this animation. + /// + /// Typically track 0 is used to completely pose the skeleton, then alpha can be used on higher tracks. It doesn't make sense + /// to use alpha on track 0 if the skeleton pose is from the last frame render. + /// + float getAlpha(); + void setAlpha(float inValue); + + /// + /// When the mix percentage (mix time / mix duration) is less than the event threshold, event timelines for the animation + /// being mixed out will be applied. Defaults to 0, so event timelines are not applied for an animation being mixed out. + float getEventThreshold(); + void setEventThreshold(float inValue); + + /// + /// When the mix percentage (mix time / mix duration) is less than the attachment threshold, attachment timelines for the + /// animation being mixed out will be applied. Defaults to 0, so attachment timelines are not applied for an animation being + /// mixed out. + float getAttachmentThreshold(); + void setAttachmentThreshold(float inValue); + + /// + /// When the mix percentage (mix time / mix duration) is less than the draw order threshold, draw order timelines for the + /// animation being mixed out will be applied. Defaults to 0, so draw order timelines are not applied for an animation being + /// mixed out. + /// + float getDrawOrderThreshold(); + void setDrawOrderThreshold(float inValue); + + /// + /// The animation queued to start after this animation, or NULL. + TrackEntry* getNext(); + + /// + /// Returns true if at least one loop has been completed. + bool isComplete(); + + /// + /// Seconds from 0 to the mix duration when mixing from the previous animation to this animation. May be slightly more than + /// TrackEntry.MixDuration when the mix is complete. + float getMixTime(); + void setMixTime(float inValue); + + /// + /// Seconds for mixing from the previous animation to this animation. Defaults to the value provided by + /// AnimationStateData based on the animation before this animation (if any). + /// + /// The mix duration can be set manually rather than use the value from AnimationStateData.GetMix. + /// In that case, the mixDuration must be set before AnimationState.update(float) is next called. + /// + /// When using AnimationState::addAnimation(int, Animation, bool, float) with a delay + /// less than or equal to 0, note the Delay is set using the mix duration from the AnimationStateData + /// + /// + /// + float getMixDuration(); + void setMixDuration(float inValue); + + /// + /// The track entry for the previous animation when mixing from the previous animation to this animation, or NULL if no + /// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a linked list. + TrackEntry* getMixingFrom(); + + /// + /// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the + /// long way around when using alpha and starting animations on other tracks. + /// + /// Mixing involves finding a rotation between two others, which has two possible solutions: the short way or the long way around. + /// The two rotations likely change over time, so which direction is the short or long way also changes. + /// If the short way was always chosen, bones would flip to the other side when that direction became the long way. + /// TrackEntry chooses the short way the first time it is applied and remembers that direction. + void resetRotationDirections(); + + void setOnAnimationEventFunc(OnAnimationEventFunc inValue); + + private: + Animation* _animation; + + TrackEntry* _next; + TrackEntry* _mixingFrom; + int _trackIndex; + + bool _loop; + float _eventThreshold, _attachmentThreshold, _drawOrderThreshold; + float _animationStart, _animationEnd, _animationLast, _nextAnimationLast; + float _delay, _trackTime, _trackLast, _nextTrackLast, _trackEnd, _timeScale; + float _alpha, _mixTime, _mixDuration, _interruptAlpha, _totalAlpha; + Vector _timelineData; + Vector _timelineDipMix; + Vector _timelinesRotation; + OnAnimationEventFunc _onAnimationEventFunc; + + /// Sets the timeline data. + /// @param to May be NULL. + TrackEntry* setTimelineData(TrackEntry* to, Vector& mixingToArray, Vector& propertyIDs); + + bool hasTimeline(int inId); + + void reset(); + }; + + class EventQueueEntry { + friend class EventQueue; + + public: + EventType _type; + TrackEntry* _entry; + Event* _event; + + EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event = NULL); + }; + + class EventQueue { + friend class AnimationState; + + private: + Vector _eventQueueEntries; + AnimationState& _state; + Pool& _trackEntryPool; + bool _drainDisabled; + + static EventQueue* newEventQueue(AnimationState& state, Pool& trackEntryPool); + + static EventQueueEntry* newEventQueueEntry(EventType eventType, TrackEntry* entry, Event* event = NULL); + + EventQueue(AnimationState& state, Pool& trackEntryPool); + + ~EventQueue(); + + void start(TrackEntry* entry); + + void interrupt(TrackEntry* entry); + + void end(TrackEntry* entry); + + void dispose(TrackEntry* entry); + + void complete(TrackEntry* entry); + + void event(TrackEntry* entry, Event* event); + + /// Raises all events in the queue and drains the queue. + void drain(); + }; + + class AnimationState { + friend class TrackEntry; + friend class EventQueue; + + public: + AnimationState(AnimationStateData& data); + + ~AnimationState(); + + /// + /// Increments the track entry times, setting queued animations as current if needed + /// @param delta delta time + void update(float delta); + + /// + /// Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the + /// animation state can be applied to multiple skeletons to pose them identically. + bool apply(Skeleton& skeleton); + + /// + /// Removes all animations from all tracks, leaving skeletons in their previous pose. + /// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose, + /// rather than leaving them in their previous pose. + void clearTracks(); + + /// + /// Removes all animations from the tracks, leaving skeletons in their previous pose. + /// It may be desired to use AnimationState.setEmptyAnimations(float) to mix the skeletons back to the setup pose, + /// rather than leaving them in their previous pose. + void clearTrack(int trackIndex); + + /// Sets an animation by name. setAnimation(int, Animation, bool) + TrackEntry* setAnimation(int trackIndex, std::string animationName, bool loop); + + /// Sets the current animation for a track, discarding any queued animations. + /// @param loop If true, the animation will repeat. + /// If false, it will not, instead its last frame is applied if played beyond its duration. + /// In either case TrackEntry.TrackEnd determines when the track is cleared. + /// @return + /// A track entry to allow further customization of animation playback. References to the track entry must not be kept + /// after AnimationState.Dispose. + TrackEntry* setAnimation(int trackIndex, Animation* animation, bool loop); + + /// Queues an animation by name. + /// addAnimation(int, Animation, bool, float) + TrackEntry* addAnimation(int trackIndex, std::string animationName, bool loop, float delay); + + /// Adds an animation to be played delay seconds after the current or last queued animation + /// for a track. If the track is empty, it is equivalent to calling setAnimation. + /// @param delay + /// Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation + /// duration of the previous track minus any mix duration plus the negative delay. + /// + /// @return A track entry to allow further customization of animation playback. References to the track entry must not be kept + /// after AnimationState.Dispose + TrackEntry* addAnimation(int trackIndex, Animation* animation, bool loop, float delay); + + /// + /// Sets an empty animation for a track, discarding any queued animations, and mixes to it over the specified mix duration. + TrackEntry* setEmptyAnimation(int trackIndex, float mixDuration); + + /// + /// Adds an empty animation to be played after the current or last queued animation for a track, and mixes to it over the + /// specified mix duration. + /// @return + /// A track entry to allow further customization of animation playback. References to the track entry must not be kept after AnimationState.Dispose. + /// + /// @param trackIndex Track number. + /// @param mixDuration Mix duration. + /// @param delay Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation + /// duration of the previous track minus any mix duration plus the negative delay. + TrackEntry* addEmptyAnimation(int trackIndex, float mixDuration, float delay); + + /// + /// Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration. + void setEmptyAnimations(float mixDuration); + + /// @return The track entry for the animation currently playing on the track, or NULL if no animation is currently playing. + TrackEntry* getCurrent(int trackIndex); + + AnimationStateData& getData(); + + /// A list of tracks that have animations, which may contain NULLs. + Vector getTracks(); + float getTimeScale(); + void setTimeScale(float inValue); + void setOnAnimationEventFunc(OnAnimationEventFunc inValue); + void setRendererObject(void* inValue); + void* getRendererObject(); + + private: + static const int Subsequent, First, Dip, DipMix; + + AnimationStateData& _data; + + Pool _trackEntryPool; + Vector _tracks; + Vector _events; + EventQueue* _queue; + + Vector _propertyIDs; + Vector _mixingTo; + bool _animationsChanged; + + void* _rendererObject; + + OnAnimationEventFunc _onAnimationEventFunc; + + float _timeScale; + + static Animation* getEmptyAnimation(); + + static void applyRotateTimeline(RotateTimeline* rotateTimeline, Skeleton& skeleton, float time, float alpha, MixPose pose, Vector& timelinesRotation, int i, bool firstFrame); + + /// Returns true when all mixing from entries are complete. + bool updateMixingFrom(TrackEntry* to, float delta); + + float applyMixingFrom(TrackEntry* to, Skeleton& skeleton, MixPose currentPose); + + void queueEvents(TrackEntry* entry, float animationTime); + + /// Sets the active TrackEntry for a given track number. + void setCurrent(int index, TrackEntry* current, bool interrupt); + + TrackEntry* expandToIndex(int index); + + /// Object-pooling version of new TrackEntry. Obtain an unused TrackEntry from the pool and clear/initialize its values. + /// @param last May be NULL. + TrackEntry* newTrackEntry(int trackIndex, Animation* animation, bool loop, TrackEntry* last); + + /// Dispose all track entries queued after the given TrackEntry. + void disposeNext(TrackEntry* entry); + + void animationsChanged(); + }; +} + +#endif /* Spine_AnimationState_h */ diff --git a/spine-cpp/spine-cpp/include/spine/AnimationStateData.h b/spine-cpp/spine-cpp/include/spine/AnimationStateData.h new file mode 100644 index 000000000..1ce86ca26 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/AnimationStateData.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_AnimationStateData_h +#define Spine_AnimationStateData_h + +#include + +#include +#include + +namespace Spine { + class SkeletonData; + class Animation; + + /// Stores mix (crossfade) durations to be applied when AnimationState animations are changed. + class AnimationStateData { + friend class AnimationState; + + public: + /// The SkeletonData to look up animations when they are specified by name. + SkeletonData& getSkeletonData(); + + /// The mix duration to use when no mix duration has been specifically defined between two animations. + float getDefaultMix(); + void setDefaultMix(float inValue); + + AnimationStateData(SkeletonData& skeletonData); + + /// Sets a mix duration by animation names. + void setMix(std::string fromName, std::string toName, float duration); + + /// Sets a mix duration when changing from the specified animation to the other. + /// See TrackEntry.MixDuration. + void setMix(Animation* from, Animation* to, float duration); + + /// + /// The mix duration to use when changing from the specified animation to the other, + /// or the DefaultMix if no mix duration has been set. + /// + float getMix(Animation* from, Animation* to); + + private: + class AnimationPair { + public: + Animation* _a1; + Animation* _a2; + + AnimationPair(Animation* a1 = NULL, Animation* a2 = NULL); + + bool operator==(const AnimationPair &other) const; + }; + + struct HashAnimationPair { + std::size_t operator()(const Spine::AnimationStateData::AnimationPair& val) const; + }; + + SkeletonData& _skeletonData; + float _defaultMix; + HashMap _animationToMixTime; + }; +} + +#endif /* Spine_AnimationStateData_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Atlas.h b/spine-cpp/spine-cpp/include/spine/Atlas.h new file mode 100644 index 000000000..1fc1afbee --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Atlas.h @@ -0,0 +1,153 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Atlas_h +#define Spine_Atlas_h + +#include +#include + +#include + +namespace Spine { + enum Format { + Format_Alpha, + Format_Intensity, + Format_LuminanceAlpha, + Format_RGB565, + Format_RGBA4444, + Format_RGB888, + Format_RGBA8888 + }; + + enum TextureFilter { + TextureFilter_Nearest, + TextureFilter_Linear, + TextureFilter_MipMap, + TextureFilter_MipMapNearestNearest, + TextureFilter_MipMapLinearNearest, + TextureFilter_MipMapNearestLinear, + TextureFilter_MipMapLinearLinear + }; + + enum TextureWrap { + TextureWrap_MirroredRepeat, + TextureWrap_ClampToEdge, + TextureWrap_Repeat + }; + + class AtlasPage { + public: + std::string name; + Format format; + TextureFilter minFilter; + TextureFilter magFilter; + TextureWrap uWrap; + TextureWrap vWrap; + void* rendererObject; + int width, height; + + AtlasPage(std::string inName) : name(inName) {} + }; + + class AtlasRegion { + public: + AtlasPage* page; + std::string name; + int x, y, width, height; + float u, v, u2, v2; + float offsetX, offsetY; + int originalWidth, originalHeight; + int index; + bool rotate; + Vector splits; + Vector pads; + }; + + class TextureLoader; + + class Atlas { + public: + Atlas(const char* path, TextureLoader& textureLoader); + + Atlas(const char* data, int length, const char* dir, TextureLoader& textureLoader); + + ~Atlas(); + + void flipV(); + + /// Returns the first region found with the specified name. This method uses std::string comparison to find the region, so the result + /// should be cached rather than calling this method multiple times. + /// @return The region, or NULL. + AtlasRegion* findRegion(std::string name); + + void dispose(); + + private: + Vector _pages; + Vector _regions; + TextureLoader& _textureLoader; + + void load(const char* begin, int length, const char* dir); + + class Str { + public: + const char* begin; + const char* end; + }; + + static void trim(Str* str); + + /// Tokenize string without modification. Returns 0 on failure + static int readLine(const char** begin, const char* end, Str* str); + + /// Moves str->begin past the first occurence of c. Returns 0 on failure + static int beginPast(Str* str, char c); + + /// Returns 0 on failure + static int readValue(const char** begin, const char* end, Str* str); + + /// Returns the number of tuple values read (1, 2, 4, or 0 for failure) + static int readTuple(const char** begin, const char* end, Str tuple[]); + + static char* mallocString(Str* str); + + static int indexOf(const char** array, int count, Str* str); + + static int equals(Str* str, const char* other); + + static int toInt(Str* str); + + static Atlas* abortAtlas(Atlas* atlas); + }; +} + +#endif /* Spine_Atlas_h */ + diff --git a/spine-cpp/spine-cpp/include/spine/AtlasAttachmentLoader.h b/spine-cpp/spine-cpp/include/spine/AtlasAttachmentLoader.h new file mode 100644 index 000000000..a43f245db --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/AtlasAttachmentLoader.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_AtlasAttachmentLoader_h +#define Spine_AtlasAttachmentLoader_h + +#include + +#include + +namespace Spine { + class Atlas; + class AtlasRegion; + + /// + /// An AttachmentLoader that configures attachments using texture regions from an Atlas. + /// See http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data about Loading Skeleton Data in the Spine Runtimes Guide. + /// + class AtlasAttachmentLoader : public AttachmentLoader { + RTTI_DECL; + + public: + AtlasAttachmentLoader(Vector& inAtlasArray); + + virtual RegionAttachment* newRegionAttachment(Skin& skin, std::string name, std::string path); + + virtual MeshAttachment* newMeshAttachment(Skin& skin, std::string name, std::string path); + + virtual BoundingBoxAttachment* newBoundingBoxAttachment(Skin& skin, std::string name); + + virtual PathAttachment* newPathAttachment(Skin& skin, std::string name); + + virtual PointAttachment* newPointAttachment(Skin& skin, std::string name); + + virtual ClippingAttachment* newClippingAttachment(Skin& skin, std::string name); + + AtlasRegion* findRegion(std::string name); + + private: + Vector _atlasArray; + }; +} + +#endif /* Spine_AtlasAttachmentLoader_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Attachment.h b/spine-cpp/spine-cpp/include/spine/Attachment.h new file mode 100644 index 000000000..8054e4d23 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Attachment.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Attachment_h +#define Spine_Attachment_h + +#include + +#include + +namespace Spine { + class Attachment { + RTTI_DECL; + + public: + Attachment(std::string name); + + const std::string& getName(); + + private: + const std::string _name; + }; +} + +#endif /* Spine_Attachment_h */ diff --git a/spine-cpp/spine-cpp/include/spine/AttachmentLoader.h b/spine-cpp/spine-cpp/include/spine/AttachmentLoader.h new file mode 100644 index 000000000..07eb4fdfb --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/AttachmentLoader.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_AttachmentLoader_h +#define Spine_AttachmentLoader_h + +#include + +#include + +namespace Spine { + class Skin; + class RegionAttachment; + class MeshAttachment; + class BoundingBoxAttachment; + class PathAttachment; + class PointAttachment; + class ClippingAttachment; + + class AttachmentLoader { + RTTI_DECL; + + AttachmentLoader(); + + virtual ~AttachmentLoader(); + + /// @return May be NULL to not load any attachment. + virtual RegionAttachment* newRegionAttachment(Skin& skin, std::string name, std::string path) = 0; + + /// @return May be NULL to not load any attachment. + virtual MeshAttachment* newMeshAttachment(Skin& skin, std::string name, std::string path) = 0; + + /// @return May be NULL to not load any attachment. + virtual BoundingBoxAttachment* newBoundingBoxAttachment(Skin& skin, std::string name) = 0; + + /// @return May be NULL to not load any attachment + virtual PathAttachment* newPathAttachment(Skin& skin, std::string name) = 0; + + virtual PointAttachment* newPointAttachment(Skin& skin, std::string name) = 0; + + virtual ClippingAttachment* newClippingAttachment(Skin& skin, std::string name) = 0; + }; +} + +#endif /* Spine_AttachmentLoader_h */ diff --git a/spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h b/spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h new file mode 100644 index 000000000..f81586ccb --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_AttachmentTimeline_h +#define Spine_AttachmentTimeline_h + +#include + +#include +#include +#include + +#include + +namespace Spine { + class Skeleton; + class Event; + + class AttachmentTimeline : public Timeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + AttachmentTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, std::string attachmentName); + + int getSlotIndex(); + void setSlotIndex(int inValue); + Vector& getFrames(); + void setFrames(Vector& inValue); // time, ... + Vector getAttachmentNames(); + void setAttachmentNames(Vector& inValue); + int getFrameCount(); + + private: + int _slotIndex; + Vector _frames; + Vector _attachmentNames; + }; +} + +#endif /* Spine_AttachmentTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/AttachmentType.h b/spine-cpp/spine-cpp/include/spine/AttachmentType.h new file mode 100644 index 000000000..76ecb42d9 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/AttachmentType.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_AttachmentType_h +#define Spine_AttachmentType_h + +namespace Spine { + enum AttachmentType { + AttachmentType_Region, + AttachmentType_Boundingbox, + AttachmentType_Mesh, + AttachmentType_Linkedmesh, + AttachmentType_Path, + AttachmentType_Point, + AttachmentType_Clipping + }; +} + +#endif /* Spine_AttachmentType_h */ diff --git a/spine-cpp/spine-cpp/include/spine/BlendMode.h b/spine-cpp/spine-cpp/include/spine/BlendMode.h new file mode 100644 index 000000000..65d8a674b --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/BlendMode.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_BlendMode_h +#define Spine_BlendMode_h + +namespace Spine { + enum BlendMode { + BlendMode_Normal = 0, + BlendMode_Additive, + BlendMode_Multiply, + BlendMode_Screen + }; +} + +#endif /* Spine_BlendMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Bone.h b/spine-cpp/spine-cpp/include/spine/Bone.h new file mode 100644 index 000000000..d2e876f8c --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Bone.h @@ -0,0 +1,206 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Bone_h +#define Spine_Bone_h + +#include + +#include + +namespace Spine { + class BoneData; + class Skeleton; + + /// Stores a bone's current pose. + /// + /// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a + /// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a + /// constraint or application code modifies the world transform after it was computed from the local transform. + class Bone : public Updatable { + friend class AnimationState; + + friend class RotateTimeline; + friend class IkConstraint; + friend class TransformConstraint; + friend class VertexAttachment; + friend class PathConstraint; + friend class Skeleton; + friend class RegionAttachment; + friend class PointAttachment; + friend class ScaleTimeline; + friend class ShearTimeline; + friend class TranslateTimeline; + + RTTI_DECL; + + public: + static void setYDown(bool inValue); + + static bool isYDown(); + + /// @param parent May be NULL. + Bone(BoneData& data, Skeleton& skeleton, Bone* parent = NULL); + + /// Same as updateWorldTransform. This method exists for Bone to implement Spine::Updatable. + virtual void update(); + + /// Computes the world transform using the parent bone and this bone's local transform. + void updateWorldTransform(); + + /// Computes the world transform using the parent bone and the specified local transform. + void updateWorldTransform(float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY); + + void setToSetupPose(); + + void worldToLocal(float worldX, float worldY, float& outLocalX, float& outLocalY); + + void localToWorld(float localX, float localY, float& outWorldX, float& outWorldY); + + float worldToLocalRotation(float worldRotation); + + float localToWorldRotation(float localRotation); + + /// + /// Rotates the world transform the specified amount and sets isAppliedValid to false. + /// + /// @param degrees Degrees. + void rotateWorld(float degrees); + + float getWorldToLocalRotationX(); + + float getWorldToLocalRotationY(); + + BoneData& getData(); + Skeleton& getSkeleton(); + Bone* getParent(); + Vector& getChildren(); + + /// The local X translation. + float getX(); + void setX(float inValue); + + /// The local Y translation. + float getY(); + void setY(float inValue); + + /// The local rotation. + float getRotation(); + void setRotation(float inValue); + + /// The local scaleX. + float getScaleX(); + void setScaleX(float inValue); + + /// The local scaleY. + float getScaleY(); + void setScaleY(float inValue); + + /// The local shearX. + float getShearX(); + void setShearX(float inValue); + + /// The local shearY. + float getShearY(); + void setShearY(float inValue); + + /// The rotation, as calculated by any constraints. + float getAppliedRotation(); + void setAppliedRotation(float inValue); + + /// The applied local x translation. + float getAX(); + void setAX(float inValue); + + /// The applied local y translation. + float getAY(); + void setAY(float inValue); + + /// The applied local scaleX. + float getAScaleX(); + void setAScaleX(float inValue); + + /// The applied local scaleY. + float getAScaleY(); + void setAScaleY(float inValue); + + /// The applied local shearX. + float getAShearX(); + void setAShearX(float inValue); + + /// The applied local shearY. + float getAShearY(); + 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(); + + /// Returns the magnitide (always positive) of the world scale X. + float getWorldScaleX(); + + /// Returns the magnitide (always positive) of the world scale Y. + float getWorldScaleY(); + + private: + static bool yDown; + + BoneData& _data; + Skeleton& _skeleton; + Bone* _parent; + Vector _children; + float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY; + float _ax, _ay, _arotation, _ascaleX, _ascaleY, _ashearX, _ashearY; + bool _appliedValid; + float _a, _b, _worldX; + float _c, _d, _worldY; + bool _sorted; + + /// Computes the individual applied transform values from the world transform. This can be useful to perform processing using + /// the applied transform after the world transform has been modified directly (eg, by a constraint).. + /// + /// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. + void updateAppliedTransform(); + }; +} + +#endif /* Spine_Bone_h */ diff --git a/spine-cpp/spine-cpp/include/spine/BoneData.h b/spine-cpp/spine-cpp/include/spine/BoneData.h new file mode 100644 index 000000000..12b060f3f --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/BoneData.h @@ -0,0 +1,107 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_BoneData_h +#define Spine_BoneData_h + +#include + +#include + +namespace Spine { + class BoneData { + friend class SkeletonBinary; + friend class SkeletonJson; + + friend class AnimationState; + + friend class RotateTimeline; + friend class ScaleTimeline; + friend class ShearTimeline; + friend class TranslateTimeline; + + public: + BoneData(int index, std::string name, BoneData* parent = NULL); + + /// The index of the bone in Skeleton.Bones + const int getIndex(); + + /// The name of the bone, which is unique within the skeleton. + const std::string& getName(); + + /// May be NULL. + BoneData* getParent(); + + float getLength(); + void setLength(float inValue); + + /// Local X translation. + float getX(); + void setX(float inValue); + + /// Local Y translation. + float getY(); + void setY(float inValue); + + /// Local rotation. + float getRotation(); + void setRotation(float inValue); + + /// Local scaleX. + float getScaleX(); + void setScaleX(float inValue); + + /// Local scaleY. + float getScaleY(); + void setScaleY(float inValue); + + /// Local shearX. + float getShearX(); + void setShearX(float inValue); + + /// Local shearY. + float getShearY(); + void setShearY(float inValue); + + /// The transform mode for how parent world transforms affect this bone. + TransformMode getTransformMode(); + void setTransformMode(TransformMode inValue); + + private: + const int _index; + const std::string _name; + BoneData* _parent; + float _length; + float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY; + TransformMode _transformMode; + }; +} + +#endif /* Spine_BoneData_h */ diff --git a/spine-cpp/spine-cpp/include/spine/BoundingBoxAttachment.h b/spine-cpp/spine-cpp/include/spine/BoundingBoxAttachment.h new file mode 100644 index 000000000..d0a707ed2 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/BoundingBoxAttachment.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_BoundingBoxAttachment_h +#define Spine_BoundingBoxAttachment_h + +#include + +namespace Spine { + /// Attachment that has a polygon for bounds checking. + class BoundingBoxAttachment : public VertexAttachment { + RTTI_DECL; + + BoundingBoxAttachment(std::string name); + }; +} + +#endif /* Spine_BoundingBoxAttachment_h */ diff --git a/spine-cpp/spine-cpp/include/spine/ClippingAttachment.h b/spine-cpp/spine-cpp/include/spine/ClippingAttachment.h new file mode 100644 index 000000000..5f4a56545 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/ClippingAttachment.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ClippingAttachment_h +#define Spine_ClippingAttachment_h + +#include + +namespace Spine { + class SlotData; + + class ClippingAttachment : public VertexAttachment { + friend class SkeletonBinary; + friend class SkeletonJson; + + friend class SkeletonClipping; + + RTTI_DECL; + + public: + ClippingAttachment(std::string name); + + SlotData* getEndSlot(); + void setEndSlot(SlotData* inValue); + + private: + SlotData* _endSlot; + }; +} + +#endif /* Spine_ClippingAttachment_h */ diff --git a/spine-cpp/spine-cpp/include/spine/ColorTimeline.h b/spine-cpp/spine-cpp/include/spine/ColorTimeline.h new file mode 100644 index 000000000..14b74a5b0 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/ColorTimeline.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ColorTimeline_h +#define Spine_ColorTimeline_h + +#include + +namespace Spine { + class ColorTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + static const int ENTRIES; + + ColorTimeline (int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float r, float g, float b, float a); + + int getSlotIndex(); + void setSlotIndex(int inValue); + Vector& getFrames(); + void setFrames(Vector& inValue); // time, r, g, b, a, ... + + protected: + static const int PREV_TIME, PREV_R, PREV_G, PREV_B, PREV_A; + static const int R, G, B, A; + + private: + int _slotIndex; + Vector _frames; + }; +} + +#endif /* Spine_ColorTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Constraint.h b/spine-cpp/spine-cpp/include/spine/Constraint.h new file mode 100644 index 000000000..4979d7602 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Constraint.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Constraint_h +#define Spine_Constraint_h + +#include + +namespace Spine { + /// The interface for all constraints. + class Constraint : public Updatable { + RTTI_DECL; + + public: + Constraint(); + + virtual ~Constraint(); + + virtual void update() = 0; + + /// The ordinal for the order a skeleton's constraints will be applied. + virtual int getOrder() = 0; + }; +} + +#endif /* Spine_Constraint_h */ diff --git a/spine-cpp/spine-cpp/include/spine/ContainerUtil.h b/spine-cpp/spine-cpp/include/spine/ContainerUtil.h new file mode 100644 index 000000000..3e764aeb3 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/ContainerUtil.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ContainerUtil_h +#define Spine_ContainerUtil_h + +#include +#include +#include + +#include +#include + +namespace Spine { + class ContainerUtil { + public: + /// Finds an item by comparing each item's name. + /// It is more efficient to cache the results of this method than to call it multiple times. + /// @return May be NULL. + template + static T* findWithName(Vector& items, std::string name) { + assert(name.length() > 0); + + for (T** i = items.begin(); i != items.end(); ++i) { + T* item = (*i); + if (item->getName() == name) { + return item; + } + } + + return NULL; + } + + /// @return -1 if the item was not found. + template + static int findIndexWithName(Vector& items, std::string name) { + assert(name.length() > 0); + + for (size_t i = 0, len = items.size(); i < len; ++i) { + T* item = items[i]; + if (item->getName() == name) { + return static_cast(i); + } + } + + return -1; + } + + /// Finds an item by comparing each item's name. + /// It is more efficient to cache the results of this method than to call it multiple times. + /// @return May be NULL. + template + static T* findWithDataName(Vector& items, std::string name) { + assert(name.length() > 0); + + for (T** i = items.begin(); i != items.end(); ++i) { + T* item = (*i); + if (item->getData().getName() == name) { + return item; + } + } + + return NULL; + } + + /// @return -1 if the item was not found. + template + static int findIndexWithDataName(Vector& items, std::string name) { + assert(name.length() > 0); + + for (size_t i = 0, len = items.size(); i < len; ++i) { + T* item = items[i]; + if (item->getData().getName() == name) { + return static_cast(i); + } + } + + return -1; + } + + template + static void cleanUpVectorOfPointers(Vector& items) { + for (size_t i = 0; i < items.size(); ) { + T* item = items[i]; + + DESTROY(T, item); + + items.erase(i); + } + } + + private: + // ctor, copy ctor, and assignment should be private in a Singleton + ContainerUtil(); + ContainerUtil(const ContainerUtil&); + ContainerUtil& operator=(const ContainerUtil&); + }; +} + +#endif /* Spine_ContainerUtil_h */ diff --git a/spine-cpp/spine-cpp/include/spine/CurveTimeline.h b/spine-cpp/spine-cpp/include/spine/CurveTimeline.h new file mode 100644 index 000000000..7df660410 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/CurveTimeline.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_CurveTimeline_h +#define Spine_CurveTimeline_h + +#include +#include + +#include + +namespace Spine { + /// Base class for frames that use an interpolation bezier curve. + class CurveTimeline : public Timeline { + RTTI_DECL; + + public: + CurveTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) = 0; + + virtual int getPropertyId() = 0; + + int getFrameCount(); + + void setLinear(int frameIndex); + + void setStepped(int frameIndex); + + /// Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + /// cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + /// the difference between the keyframe's values. + void setCurve(int frameIndex, float cx1, float cy1, float cx2, float cy2); + + float getCurvePercent(int frameIndex, float percent); + + float getCurveType(int frameIndex); + + protected: + static const float LINEAR; + static const float STEPPED; + static const float BEZIER; + static const int BEZIER_SIZE; + + private: + Vector _curves; // type, x, y, ... + }; +} + +#endif /* Spine_CurveTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/DeformTimeline.h b/spine-cpp/spine-cpp/include/spine/DeformTimeline.h new file mode 100644 index 000000000..031c420f3 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/DeformTimeline.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_DeformTimeline_h +#define Spine_DeformTimeline_h + +#include + +namespace Spine { + class VertexAttachment; + + class DeformTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + DeformTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, Vector& vertices); + + int getSlotIndex(); + void setSlotIndex(int inValue); + Vector& getFrames(); + void setFrames(Vector& inValue); // time, ... + Vector< Vector >& getVertices(); + void setVertices(Vector< Vector >& inValue); + VertexAttachment* getAttachment(); + void setAttachment(VertexAttachment* inValue); + + private: + int _slotIndex; + Vector _frames; + Vector< Vector > _frameVertices; + VertexAttachment* _attachment; + }; +} + +#endif /* Spine_DeformTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/DrawOrderTimeline.h b/spine-cpp/spine-cpp/include/spine/DrawOrderTimeline.h new file mode 100644 index 000000000..263336954 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/DrawOrderTimeline.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_DrawOrderTimeline_h +#define Spine_DrawOrderTimeline_h + +#include + +namespace Spine { + class DrawOrderTimeline : public Timeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + DrawOrderTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + /// @param drawOrder May be NULL to use bind pose draw order + void setFrame(int frameIndex, float time, Vector& drawOrder); + + Vector& getFrames(); + void setFrames(Vector& inValue); // time, ... + Vector< Vector >& getDrawOrders(); + void setDrawOrders(Vector< Vector >& inValue); + int getFrameCount(); + + private: + Vector _frames; + Vector< Vector > _drawOrders; + }; +} + +#endif /* Spine_DrawOrderTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Event.h b/spine-cpp/spine-cpp/include/spine/Event.h new file mode 100644 index 000000000..179c74028 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Event.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Event_h +#define Spine_Event_h + +#include + +namespace Spine { + class EventData; + + /// Stores the current pose values for an Event. + class Event { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class AnimationState; + + public: + Event(float time, const EventData& data); + + const EventData& getData(); + + /// The animation time this event was keyed. + float getTime(); + + int getIntValue(); + void setIntValue(int inValue); + + float getFloatValue(); + void setFloatValue(int inValue); + + std::string getStringValue(); + void setStringValue(std::string inValue); + + private: + const EventData& _data; + const float _time; + int _intValue; + float _floatValue; + std::string _stringValue; + }; +} + +#endif /* Spine_Event_h */ diff --git a/spine-cpp/spine-cpp/include/spine/EventData.h b/spine-cpp/spine-cpp/include/spine/EventData.h new file mode 100644 index 000000000..691c5acbf --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/EventData.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_EventData_h +#define Spine_EventData_h + +#include + +namespace Spine { + /// Stores the setup pose values for an Event. + class EventData { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class Event; + + public: + EventData(std::string name); + + /// The name of the event, which is unique within the skeleton. + const std::string& getName(); + + int getIntValue(); + void setIntValue(int inValue); + + float getFloatValue(); + void setFloatValue(float inValue); + + std::string getStringValue(); + void setStringValue(std::string inValue); + + private: + const std::string _name; + int _intValue; + float _floatValue; + std::string _stringValue; + }; +} + +#endif /* Spine_EventData_h */ diff --git a/spine-cpp/spine-cpp/include/spine/EventTimeline.h b/spine-cpp/spine-cpp/include/spine/EventTimeline.h new file mode 100644 index 000000000..51f290529 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/EventTimeline.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_EventTimeline_h +#define Spine_EventTimeline_h + +#include + +namespace Spine { + class EventTimeline : public Timeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + EventTimeline(int frameCount); + + ~EventTimeline(); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, Event* event); + + Vector getFrames(); + void setFrames(Vector& inValue); + Vector& getEvents(); + void setEvents(Vector& inValue); + int getFrameCount(); + + private: + Vector _frames; + Vector _events; + }; +} + +#endif /* Spine_EventTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Extension.h b/spine-cpp/spine-cpp/include/spine/Extension.h new file mode 100644 index 000000000..752d445e0 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Extension.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Extension_h +#define Spine_Extension_h + +#include + +#define SPINE_EXTENSION (SpineExtension::getInstance()) + +/* All allocation uses these. */ +#define MALLOC(TYPE,COUNT) ((TYPE*)SPINE_EXTENSION->spineAlloc(sizeof(TYPE) * (COUNT), __FILE__, __LINE__)) +#define CALLOC(TYPE,COUNT) ((TYPE*)SPINE_EXTENSION->spineCalloc(COUNT, sizeof(TYPE), __FILE__, __LINE__)) +#define NEW(TYPE) CALLOC(TYPE,1) +#define REALLOC(PTR,TYPE,COUNT) ((TYPE*)SPINE_EXTENSION->spineRealloc(PTR, sizeof(TYPE) * (COUNT), __FILE__, __LINE__)) + +/* Frees memory. Can be used on const types. */ +#define FREE(VALUE) SPINE_EXTENSION->spineFree((void*)VALUE) + +/* Call destructor and then frees memory. Can be used on const types. */ +#define DESTROY(TYPE,VALUE) VALUE->~TYPE(); SPINE_EXTENSION->spineFree((void*)VALUE) + +namespace Spine { + class SpineExtension { + public: + static void setInstance(SpineExtension* inSpineExtension); + + static SpineExtension* getInstance(); + + virtual ~SpineExtension(); + + /// Implement this function to use your own memory allocator + virtual void* spineAlloc(size_t size, const char* file, int line) = 0; + + virtual void* spineCalloc(size_t num, size_t size, const char* file, int line) = 0; + + virtual void* spineRealloc(void* ptr, size_t size, const char* file, int line) = 0; + + /// If you provide a spineAllocFunc, you should also provide a spineFreeFunc + virtual void spineFree(void* mem) = 0; + + virtual char* spineReadFile(const char* path, int* length); + + protected: + SpineExtension(); + + private: + static SpineExtension* _instance; + }; + + class DefaultSpineExtension : public SpineExtension { + public: + static DefaultSpineExtension* getInstance(); + + virtual ~DefaultSpineExtension(); + + virtual void* spineAlloc(size_t size, const char* file, int line); + + virtual void* spineCalloc(size_t num, size_t size, const char* file, int line); + + virtual void* spineRealloc(void* ptr, size_t size, const char* file, int line); + + virtual void spineFree(void* mem); + + protected: + DefaultSpineExtension(); + }; +} + +#endif /* Spine_Extension_h */ diff --git a/spine-cpp/spine-cpp/include/spine/HashMap.h b/spine-cpp/spine-cpp/include/spine/HashMap.h new file mode 100755 index 000000000..07a9b2ef3 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/HashMap.h @@ -0,0 +1,300 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_HashMap_h +#define Spine_HashMap_h + +#include +#include + +namespace Spine { + template + class HashMap { + private: + class Entry; + + public: + class Iterator { + friend class HashMap; + + public: + Iterator(Entry* entry = NULL) : _entry(entry) { + // Empty + } + + Iterator& operator++() { + _entry = _entry->next; + return *this; + } + + Iterator& operator--() { + _entry = _entry->prev; + return *this; + } + + bool operator==(const Iterator& p) const { + return _entry == p._entry; + } + + bool operator!=(const Iterator& p) const { + return _entry != p._entry; + } + + K& first() { + return _entry->_key; + } + + V& second() { + return _entry->_value; + } + + private: + Entry* _entry; + }; + + HashMap(size_t capacity = 65535) : + _hashFunction(), + _capacity(capacity), + _header(), + _trailer(), + _hashSize(0) { + _hashTable.reserve(capacity); + for (int i = 0; i < _capacity; ++i) { + _hashTable.push_back(Entry()); + } + + _header.prev = &_header; + _header.next = &_trailer; + _trailer.prev = &_header; + _trailer.next = &_trailer; + } + + ~HashMap() { + _hashSize = 0; + } + + size_t size() { + return _hashSize; + } + + Iterator begin() { + return Iterator(_header.next); + } + + Iterator end() { + return Iterator(&_trailer); + } + + Iterator rbegin() { + return Iterator(_trailer.prev); + } + + Iterator rend() { + return Iterator(_header); + } + + std::pair insert(const K& key, const V& value) { + Iterator iter = find(key); + + if (iter._entry != &_trailer) { + return std::make_pair(iter, false); + } + + size_t index = hash(key); + + Entry* entry = NEW(Entry); + new (entry) Entry(); + entry->_key = key; + entry->_value = value; + + _hashSize++; + + if (_header.next == (&_trailer)) { + _hashTable[index].next = entry; + _hashTable[index].prev = entry; + _header.next = entry; + entry->prev = &_header; + entry->next = &_trailer; + _trailer.prev = entry; + + return std::make_pair(Iterator(entry), true); + } + + if (_hashTable[index].next == NULL) { + _hashTable[index].next = entry; + _hashTable[index].prev = entry; + if (index < hash(_header.next->_key)) { + entry->next = _header.next; + entry->prev = &_header; + _header.next->prev = entry; + _header.next = entry; + } + else { + entry->next = &_trailer; + entry->prev = _trailer.prev; + _trailer.prev = entry; + entry->prev->next = entry; + } + + return std::make_pair(Iterator(entry), true); + } + + if (index == hash(_header.next->_key)) { + _header.next = entry; + entry->next = _hashTable[index].next; + entry->prev = &_header; + _hashTable[index].next->prev = entry; + _hashTable[index].next = entry; + } + else { + entry->next = _hashTable[index].next; + entry->prev = _hashTable[index].next->prev; + entry->next->prev = entry; + entry->prev->next = entry; + _hashTable[index].next = entry; + } + + return std::make_pair(Iterator(entry), true); + } + + Iterator find(const K& key) { + const size_t index = hash(key); + Iterator iter(_hashTable[index].next); + + if (iter._entry != NULL) { + for ( ; hash(iter._entry->_key) == index ; ++iter) { + if (iter._entry->_key == key) { + return iter; + } + } + } + + return Iterator(&_trailer); + } + + Iterator erase(Iterator pos) { + if (pos._entry != &_header && pos._entry != &_trailer) { + Entry* next = pos._entry->next; + + size_t index = hash(pos._entry->_key); + + if (_hashTable[index].next == pos._entry && _hashTable[index].prev == pos._entry) { + _hashTable[index].next = NULL; + _hashTable[index].prev = NULL; + + if (_header.next == pos._entry) { + _header.next = pos._entry->next; + pos._entry->next->prev = &_header; + } + else if (_trailer.prev == pos._entry) { + _trailer.prev = pos._entry->prev; + pos._entry->prev->next = &_trailer; + } + else { + pos._entry->prev->next = pos._entry->next; + pos._entry->next->prev = pos._entry->prev; + } + + DESTROY(Entry, pos._entry); + } + else if (_hashTable[index].next == pos._entry) { + _hashTable[index].next = pos._entry->next; + if (_header.next == pos._entry) { + _header.next = pos._entry->next; + pos._entry->next->prev = &_header; + } + else { + pos._entry->prev->next = pos._entry->next; + pos._entry->next->prev = pos._entry->prev; + } + + DESTROY(Entry, pos._entry); + } + else if (_hashTable[index].prev == pos._entry) { + _hashTable[index].prev = pos._entry->prev; + if (_trailer.prev == pos._entry) { + _trailer.prev = pos._entry->prev; + pos._entry->prev->next = &_trailer; + } + else { + pos._entry->prev->next = pos._entry->next; + pos._entry->next->prev = pos._entry->prev; + } + + DESTROY(Entry, pos._entry); + } + else { + pos._entry->prev->next = pos._entry->next; + pos._entry->next->prev = pos._entry->prev; + + DESTROY(Entry, pos._entry); + } + + _hashSize--; + + return Iterator(next); + } + + return Iterator(&_trailer); + } + + V operator[](const K& key) { + Iterator iter = find(key); + + if (iter._entry != _trailer) { + return iter._entry->_value; + } + + return V(); + } + + private: + class Entry { + public: + K _key; + V _value; + Entry* next; + Entry* prev; + }; + + const H _hashFunction; + const size_t _capacity; + Vector _hashTable; + Entry _header; + Entry _trailer; + size_t _hashSize; + + size_t hash(const K& key) { + return _hashFunction(key) % _capacity; + } + }; +} + +#endif /* Spine_HashMap_h */ diff --git a/spine-cpp/spine-cpp/include/spine/IkConstraint.h b/spine-cpp/spine-cpp/include/spine/IkConstraint.h new file mode 100644 index 000000000..528ebf45a --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/IkConstraint.h @@ -0,0 +1,90 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_IkConstraint_h +#define Spine_IkConstraint_h + +#include + +#include + +namespace Spine { + class IkConstraintData; + class Skeleton; + class Bone; + + class IkConstraint : public Constraint { + friend class Skeleton; + friend class IkConstraintTimeline; + + RTTI_DECL; + + public: + /// Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified + /// in the world coordinate system. + static void apply(Bone& bone, float targetX, float targetY, float alpha); + + /// Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as + /// possible. The target is specified in the world coordinate system. + /// @param child A direct descendant of the parent bone. + static void apply(Bone& parent, Bone& child, float targetX, float targetY, int bendDir, float alpha); + + IkConstraint(IkConstraintData& data, Skeleton& skeleton); + + /// Applies the constraint to the constrained bones. + void apply(); + + virtual void update(); + + virtual int getOrder(); + + IkConstraintData& getData(); + + Vector& getBones(); + + Bone* getTarget(); + void setTarget(Bone* inValue); + + int getBendDirection(); + void setBendDirection(int inValue); + + float getMix(); + void setMix(float inValue); + + private: + IkConstraintData& _data; + Vector _bones; + float _mix; + int _bendDirection; + Bone* _target; + }; +} + +#endif /* Spine_IkConstraint_h */ diff --git a/spine-cpp/spine-cpp/include/spine/IkConstraintData.h b/spine-cpp/spine-cpp/include/spine/IkConstraintData.h new file mode 100644 index 000000000..119563d2a --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/IkConstraintData.h @@ -0,0 +1,81 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_IkConstraintData_h +#define Spine_IkConstraintData_h + +#include + +#include + +namespace Spine { + class BoneData; + + class IkConstraintData { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class IkConstraint; + friend class Skeleton; + friend class IkConstraintTimeline; + + public: + IkConstraintData(std::string name); + + /// The IK constraint's name, which is unique within the skeleton. + const std::string& getName(); + + int getOrder(); + void setOrder(int inValue); + + /// The bones that are constrained by this IK Constraint. + Vector& getBones(); + + /// The bone that is the IK target. + BoneData* getTarget(); + void setTarget(BoneData* inValue); + + /// Controls the bend direction of the IK bones, either 1 or -1. + int getBendDirection(); + void setBendDirection(int inValue); + + float getMix(); + void setMix(float inValue); + + private: + const std::string _name; + int _order; + Vector _bones; + BoneData* _target; + int _bendDirection; + float _mix; + }; +} + +#endif /* Spine_IkConstraintData_h */ diff --git a/spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h b/spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h new file mode 100644 index 000000000..bbc75df6b --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_IkConstraintTimeline_h +#define Spine_IkConstraintTimeline_h + +#include + +namespace Spine { + class IkConstraintTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + static const int ENTRIES; + + IkConstraintTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time, mix and bend direction of the specified keyframe. + void setFrame (int frameIndex, float time, float mix, int bendDirection); + + private: + static const int PREV_TIME, PREV_MIX, PREV_BEND_DIRECTION; + static const int MIX, BEND_DIRECTION; + + Vector _frames; + int _ikConstraintIndex; + }; +} + +#endif /* Spine_IkConstraintTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Json.h b/spine-cpp/spine-cpp/include/spine/Json.h new file mode 100644 index 000000000..083e22322 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Json.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Json_h +#define Spine_Json_h + +#ifndef SPINE_JSON_HAVE_PREV +/* Spine doesn't use the "prev" link in the Json sibling lists. */ +#define SPINE_JSON_HAVE_PREV 0 +#endif + +namespace Spine { + class Json { + friend class SkeletonJson; + + public: + /* Json Types: */ + static const int JSON_FALSE; + static const int JSON_TRUE; + static const int JSON_NULL; + static const int JSON_NUMBER; + static const int JSON_STRING; + static const int JSON_ARRAY; + static const int JSON_OBJECT; + + /* Get item "string" from object. Case insensitive. */ + static Json* getItem(Json *object, const char* string); + + static const char* getString(Json *object, const char* name, const char* defaultValue); + + static float getFloat(Json *object, const char* name, float defaultValue); + + static int getInt(Json *object, const char* name, int defaultValue); + + /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */ + static const char* getError(); + + /* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */ + Json(const char* value); + + ~Json(); + + private: + static const char* _error; + + Json* _next; +#if SPINE_JSON_HAVE_PREV + Json* _prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItem */ +#endif + Json* _child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int _type; /* The type of the item, as above. */ + int _size; /* The number of children. */ + + const char* _valueString; /* The item's string, if type==JSON_STRING */ + int _valueInt; /* The item's number, if type==JSON_NUMBER */ + float _valueFloat; /* The item's number, if type==JSON_NUMBER */ + + const char* _name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + + /* Utility to jump whitespace and cr/lf */ + static const char* skip(const char* inValue); + + /* Parser core - when encountering text, process appropriately. */ + static const char* parseValue(Json *item, const char* value); + + /* Parse the input text into an unescaped cstring, and populate item. */ + static const char* parseString(Json *item, const char* str); + + /* Parse the input text to generate a number, and populate the result into item. */ + static const char* parseNumber(Json *item, const char* num); + + /* Build an array from input text. */ + static const char* parseArray(Json *item, const char* value); + + /* Build an object from the text. */ + static const char* parseObject(Json *item, const char* value); + + static int json_strcasecmp(const char* s1, const char* s2); + }; +} + +#endif /* Spine_Json_h */ diff --git a/spine-cpp/spine-cpp/include/spine/LinkedMesh.h b/spine-cpp/spine-cpp/include/spine/LinkedMesh.h new file mode 100644 index 000000000..f8fde0944 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/LinkedMesh.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_LinkedMesh_h +#define Spine_LinkedMesh_h + +#include + +namespace Spine { + class MeshAttachment; + + class LinkedMesh { + friend class SkeletonBinary; + friend class SkeletonJson; + + public: + LinkedMesh(MeshAttachment* mesh, std::string skin, int slotIndex, std::string parent); + + private: + MeshAttachment* _mesh; + std::string _skin; + int _slotIndex; + std::string _parent; + }; +} + +#endif /* Spine_LinkedMesh_h */ diff --git a/spine-cpp/spine-cpp/include/spine/MathUtil.h b/spine-cpp/spine-cpp/include/spine/MathUtil.h new file mode 100644 index 000000000..cd7d32f42 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/MathUtil.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_MathUtil_h +#define Spine_MathUtil_h + +#include +#include + +#define SPINE_PI 3.1415927f +#define SPINE_PI_2 SPINE_PI * 2 +#define RadDeg 180.0f / SPINE_PI +#define DegRad SPINE_PI / 180.0f +#define SIN_BITS 14 // 16KB. Adjust for accuracy. +#define SIN_MASK ~(-(1 << SIN_BITS)) +#define SIN_COUNT SIN_MASK + 1 +#define RadFull SPINE_PI * 2 +#define DegFull 360 +#define RadToIndex SIN_COUNT / RadFull +#define DegToIndex SIN_COUNT / DegFull +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +namespace Spine { + template + int sign(T val) { + return (T(0) < val) - (val < T(0)); + } + + inline bool areFloatsPracticallyEqual(float A, float B, float maxDiff = 0.0000000000000001f, float maxRelDiff = FLT_EPSILON) { + // Check if the numbers are really close -- needed + // when comparing numbers near zero. + float diff = fabs(A - B); + if (diff <= maxDiff) { + return true; + } + + A = fabs(A); + B = fabs(B); + + float largest = (B > A) ? B : A; + + if (diff <= largest * maxRelDiff) { + return true; + } + + return false; + } + + inline float clamp(float x, float lower, float upper) { + return fminf(upper, fmaxf(x, lower)); + } + + class MathUtil { + public: + MathUtil(); + + /// Returns the sine in radians from a lookup table. + static float sin(float radians); + + /// Returns the cosine in radians from a lookup table. + static float cos(float radians); + + /// Returns the sine in radians from a lookup table. + static float sinDeg(float degrees); + + /// Returns the cosine in radians from a lookup table. + static float cosDeg(float degrees); + + /// Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323 + /// degrees), largest error of 0.00488 radians (0.2796 degrees). + static float atan2(float y, float x); + + private: + static float SIN_TABLE[SIN_COUNT]; + }; +} + +#endif /* Spine_MathUtil_h */ diff --git a/spine-cpp/spine-cpp/include/spine/MeshAttachment.h b/spine-cpp/spine-cpp/include/spine/MeshAttachment.h new file mode 100644 index 000000000..101f6dcf9 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/MeshAttachment.h @@ -0,0 +1,155 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_MeshAttachment_h +#define Spine_MeshAttachment_h + +#include + +#include + +#include + +namespace Spine { + /// Attachment that displays a texture region using a mesh. + class MeshAttachment : public VertexAttachment { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class AtlasAttachmentLoader; + + RTTI_DECL; + + public: + MeshAttachment(std::string name); + + void updateUVs(); + + virtual bool applyDeform(VertexAttachment* sourceAttachment); + + int getHullLength(); + void setHullLength(float inValue); + + Vector& getRegionUVs(); + void setRegionUVs(Vector& inValue); + + /// The UV pair for each vertex, normalized within the entire texture. See also MeshAttachment::updateUVs + Vector& getUVs(); + void setUVs(Vector& inValue); + + Vector& getTriangles(); + void setTriangles(Vector& inValue); + + float getR(); + void setR(float inValue); + float getG(); + void setG(float inValue); + float getB(); + void setB(float inValue); + float getA(); + void setA(float inValue); + + std::string getPath(); + void setPath(std::string inValue); + void* getRendererObject(); + void setRendererObject(void* inValue); + + float getRegionU(); + void setRegionU(float inValue); + + float getRegionV(); + void setRegionV(float inValue); + + float getRegionU2(); + void setRegionU2(float inValue); + + float getRegionV2(); + void setRegionV2(float inValue); + + bool getRegionRotate(); + void setRegionRotate(float inValue); + + float getRegionOffsetX(); + void setRegionOffsetX(float inValue); + + // Pixels stripped from the bottom left, unrotated. + float getRegionOffsetY(); + void setRegionOffsetY(float inValue); + + float getRegionWidth(); + void setRegionWidth(float inValue); + + // Unrotated, stripped size. + float getRegionHeight(); + void setRegionHeight(float inValue); + + float getRegionOriginalWidth(); + void setRegionOriginalWidth(float inValue); + + // Unrotated, unstripped size. + float getRegionOriginalHeight(); + void setRegionOriginalHeight(float inValue); + + bool getInheritDeform(); + void setInheritDeform(bool inValue); + + MeshAttachment* getParentMesh(); + void setParentMesh(MeshAttachment* inValue); + + // Nonessential. + Vector& getEdges(); + void setEdges(Vector& inValue); + float getWidth(); + void setWidth(float inValue); + float getHeight(); + void setHeight(float inValue); + + private: + float _regionOffsetX, _regionOffsetY, _regionWidth, _regionHeight, _regionOriginalWidth, _regionOriginalHeight; + MeshAttachment* _parentMesh; + Vector _uvs; + Vector _regionUVs; + Vector _triangles; + Vector _edges; + void* _rendererObject; + std::string _path; + float _regionU; + float _regionV; + float _regionU2; + float _regionV2; + float _width; + float _height; + float _r, _g, _b, _a; + int _hullLength; + bool _inheritDeform; + bool _regionRotate; + }; +} + +#endif /* Spine_MeshAttachment_h */ diff --git a/spine-cpp/spine-cpp/include/spine/MixDirection.h b/spine-cpp/spine-cpp/include/spine/MixDirection.h new file mode 100644 index 000000000..68f937a8b --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/MixDirection.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_MixDirection_h +#define Spine_MixDirection_h + +namespace Spine { + /// + /// Indicates whether a timeline's alpha is mixing out over time toward 0 (the setup or current pose) or mixing in toward 1 (the timeline's pose). + /// See also Timeline::apply(Skeleton&, float, float, Vector&, float, MixPose, MixDirection) + enum MixDirection { + MixDirection_In = 0, + MixDirection_Out + }; +} + +#endif /* Spine_MixDirection_h */ diff --git a/spine-cpp/spine-cpp/include/spine/MixPose.h b/spine-cpp/spine-cpp/include/spine/MixPose.h new file mode 100644 index 000000000..7156a8fdc --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/MixPose.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_MixPose_h +#define Spine_MixPose_h + +namespace Spine { + /// + /// Controls how a timeline is mixed with the setup or current pose. + /// See also Timeline::apply(Skeleton&, float, float, Vector&, float, MixPose, MixDirection) + enum MixPose { + /// The timeline value is mixed with the setup pose (the current pose is not used). + MixPose_Setup = 0, + /// The timeline value is mixed with the current pose. The setup pose is used as the timeline value before the first key, + /// except for timelines which perform instant transitions, such as DrawOrderTimeline or AttachmentTimeline. + MixPose_Current, + /// The timeline value is mixed with the current pose. No change is made before the first key (the current pose is kept until the first key). + MixPose_CurrentLayered + }; +} + +#endif /* Spine_MixPose_h */ diff --git a/spine-cpp/spine-cpp/include/spine/PathAttachment.h b/spine-cpp/spine-cpp/include/spine/PathAttachment.h new file mode 100644 index 000000000..4463f1d01 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/PathAttachment.h @@ -0,0 +1,61 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathAttachment_h +#define Spine_PathAttachment_h + +#include + +namespace Spine { + class PathAttachment : public VertexAttachment { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + PathAttachment(std::string name); + + /// The length in the setup pose from the start of the path to the end of each curve. + Vector& getLengths(); + void setLengths(Vector inValue); + bool isClosed(); + void setClosed(bool inValue); + bool isConstantSpeed(); + void setConstantSpeed(bool inValue); + + private: + Vector _lengths; + bool _closed; + bool _constantSpeed; + }; +} + +#endif /* Spine_PathAttachment_h */ diff --git a/spine-cpp/spine-cpp/include/spine/PathConstraint.h b/spine-cpp/spine-cpp/include/spine/PathConstraint.h new file mode 100644 index 000000000..abb073a82 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/PathConstraint.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathConstraint_h +#define Spine_PathConstraint_h + +#include + +#include + +namespace Spine { + class PathConstraintData; + class Skeleton; + class PathAttachment; + class Bone; + class Slot; + + class PathConstraint : public Constraint { + friend class Skeleton; + friend class PathConstraintMixTimeline; + friend class PathConstraintPositionTimeline; + friend class PathConstraintSpacingTimeline; + + RTTI_DECL; + + public: + PathConstraint(PathConstraintData& data, Skeleton& skeleton); + + /// Applies the constraint to the constrained bones. + void apply(); + + virtual void update(); + + virtual int getOrder(); + + float getPosition(); + void setPosition(float inValue); + + float getSpacing(); + void setSpacing(float inValue); + + float getRotateMix(); + void setRotateMix(float inValue); + + float getTranslateMix(); + void setTranslateMix(float inValue); + + Vector& getBones(); + + Slot* getTarget(); + void setTarget(Slot* inValue); + + PathConstraintData& getData(); + + private: + static const float EPSILON; + static const int NONE; + static const int BEFORE; + static const int AFTER; + + PathConstraintData& _data; + Vector _bones; + Slot* _target; + float _position, _spacing, _rotateMix, _translateMix; + + Vector _spaces; + Vector _positions; + Vector _world; + Vector _curves; + Vector _lengths; + Vector _segments; + + Vector computeWorldPositions(PathAttachment& path, int spacesCount, bool tangents, bool percentPosition, bool percentSpacing); + + static void addBeforePosition(float p, Vector& temp, int i, Vector& output, int o); + + static void addAfterPosition(float p, Vector& temp, int i, Vector& output, int o); + + static void addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, Vector& output, int o, bool tangents); + }; +} + +#endif /* Spine_PathConstraint_h */ diff --git a/spine-cpp/spine-cpp/include/spine/PathConstraintData.h b/spine-cpp/spine-cpp/include/spine/PathConstraintData.h new file mode 100644 index 000000000..c4e3812e5 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/PathConstraintData.h @@ -0,0 +1,105 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathConstraintData_h +#define Spine_PathConstraintData_h + +#include +#include +#include +#include + +#include + +namespace Spine { + class BoneData; + class SlotData; + + class PathConstraintData { + friend class SkeletonBinary; + friend class SkeletonJson; + + friend class PathConstraint; + friend class Skeleton; + friend class PathConstraintMixTimeline; + friend class PathConstraintPositionTimeline; + friend class PathConstraintSpacingTimeline; + + public: + PathConstraintData(std::string name); + + const std::string& getName(); + + int getOrder(); + void setOrder(int inValue); + + Vector& getBones(); + + SlotData* getTarget(); + void setTarget(SlotData* inValue); + + PositionMode getPositionMode(); + void setPositionMode(PositionMode inValue); + + SpacingMode getSpacingMode(); + void setSpacingMode(SpacingMode inValue); + + RotateMode getRotateMode(); + void setRotateMode(RotateMode inValue); + + float getOffsetRotation(); + void setOffsetRotation(float inValue); + + float getPosition(); + void setPosition(float inValue); + + float getSpacing(); + void setSpacing(float inValue); + + float getRotateMix(); + void setRotateMix(float inValue); + + float getTranslateMix(); + void setTranslateMix(float inValue); + + private: + const std::string _name; + int _order; + Vector _bones; + SlotData* _target; + PositionMode _positionMode; + SpacingMode _spacingMode; + RotateMode _rotateMode; + float _offsetRotation; + float _position, _spacing, _rotateMix, _translateMix; + }; +} + +#endif /* Spine_PathConstraintData_h */ diff --git a/spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h b/spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h new file mode 100644 index 000000000..5e93c1235 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathConstraintMixTimeline_h +#define Spine_PathConstraintMixTimeline_h + +#include + +namespace Spine { + class PathConstraintMixTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + static const int ENTRIES; + + PathConstraintMixTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + private: + static const int PREV_TIME, PREV_ROTATE, PREV_TRANSLATE; + static const int ROTATE, TRANSLATE; + + Vector _frames; + int _pathConstraintIndex; + + /// Sets the time and mixes of the specified keyframe. + void setFrame(int frameIndex, float time, float rotateMix, float translateMix); + }; +} + +#endif /* Spine_PathConstraintMixTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h b/spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h new file mode 100644 index 000000000..fb4d222e1 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathConstraintPositionTimeline_h +#define Spine_PathConstraintPositionTimeline_h + +#include + +namespace Spine { + class PathConstraintPositionTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + static const int ENTRIES; + + PathConstraintPositionTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float value); + + protected: + static const int PREV_TIME, PREV_VALUE; + static const int VALUE; + + Vector _frames; + int _pathConstraintIndex; + }; +} + +#endif /* Spine_PathConstraintPositionTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/PathConstraintSpacingTimeline.h b/spine-cpp/spine-cpp/include/spine/PathConstraintSpacingTimeline.h new file mode 100644 index 000000000..ca723f907 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/PathConstraintSpacingTimeline.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PathConstraintSpacingTimeline_h +#define Spine_PathConstraintSpacingTimeline_h + +#include + +namespace Spine { + class PathConstraintSpacingTimeline : public PathConstraintPositionTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + PathConstraintSpacingTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + }; +} + +#endif /* Spine_PathConstraintSpacingTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/PointAttachment.h b/spine-cpp/spine-cpp/include/spine/PointAttachment.h new file mode 100644 index 000000000..eba6a0f8d --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/PointAttachment.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PointAttachment_h +#define Spine_PointAttachment_h + +#include + +namespace Spine { + class Bone; + + /// + /// An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be + /// used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a + /// skin. + /// + /// See http://esotericsoftware.com/spine-point-attachments for Point Attachments in the Spine User Guide. + /// + class PointAttachment : public Attachment { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + PointAttachment(std::string name); + + void computeWorldPosition(Bone& bone, float& ox, float& oy); + + float computeWorldRotation(Bone& bone); + + float getX(); + void setX(float inValue); + + float getY(); + void setY(float inValue); + + float getRotation(); + void setRotation(float inValue); + + private: + float _x, _y, _rotation; + }; +} + +#endif /* Spine_PointAttachment_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Pool.h b/spine-cpp/spine-cpp/include/spine/Pool.h new file mode 100644 index 000000000..18f17a6e9 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Pool.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Pool_h +#define Spine_Pool_h + +#include +#include +#include + +namespace Spine { + template + class Pool { + public: + Pool() { + // Empty + } + + ~Pool() { + ContainerUtil::cleanUpVectorOfPointers(_objects); + } + + T* obtain() { + if (_objects.size() > 0) { + T** object = _objects.begin(); + T* ret = *object; + _objects.erase(0); + + return ret; + } + else { + T* ret = NEW(T); + new (ret) T(); + + return ret; + } + } + + void free(T* object) { + if (!_objects.contains(object)) { + _objects.push_back(object); + } + } + + private: + Vector _objects; + }; +} + +#endif /* Spine_Pool_h */ diff --git a/spine-cpp/spine-cpp/include/spine/PositionMode.h b/spine-cpp/spine-cpp/include/spine/PositionMode.h new file mode 100644 index 000000000..c46d3002d --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/PositionMode.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_PositionMode_h +#define Spine_PositionMode_h + +namespace Spine { + enum PositionMode { + PositionMode_Fixed = 0, + PositionMode_Percent + }; +} + +#endif /* Spine_PositionMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/RTTI.h b/spine-cpp/spine-cpp/include/spine/RTTI.h new file mode 100644 index 000000000..73db676cc --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/RTTI.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_RTTI_h +#define Spine_RTTI_h + +#include + +namespace Spine { + class RTTI { + public: + RTTI(const std::string& className); + + RTTI(const std::string& className, const RTTI& baseRTTI); + + const std::string& getClassName() const; + + bool isExactly(const RTTI& rtti) const; + + bool derivesFrom(const RTTI& rtti) const; + + private: + // Prevent copying + RTTI(const RTTI& obj); + RTTI& operator=(const RTTI& obj); + + const std::string m_className; + const RTTI *m_pBaseRTTI; + }; +} + +#define RTTI_DECL \ +public: \ +static const Spine::RTTI rtti; \ +virtual const Spine::RTTI& getRTTI(); + +#define RTTI_IMPL_NOPARENT(name) \ +const Spine::RTTI name::rtti(#name); \ +const Spine::RTTI& name::getRTTI() { return rtti; } + +#define RTTI_IMPL(name,parent) \ +const Spine::RTTI name::rtti(#name, parent::rtti); \ +const Spine::RTTI& name::getRTTI() { return rtti; } + +#endif /* Spine_RTTI_h */ + diff --git a/spine-cpp/spine-cpp/include/spine/RegionAttachment.h b/spine-cpp/spine-cpp/include/spine/RegionAttachment.h new file mode 100644 index 000000000..e23ea4986 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/RegionAttachment.h @@ -0,0 +1,141 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_RegionAttachment_h +#define Spine_RegionAttachment_h + +#include + +#include + +#include + +#define NUM_UVS 8 + +namespace Spine { + class Bone; + + /// Attachment that displays a texture region. + class RegionAttachment : public Attachment { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class AtlasAttachmentLoader; + + RTTI_DECL; + + public: + RegionAttachment(std::string name); + + void updateOffset(); + + void setUVs(float u, float v, float u2, float v2, bool rotate); + + /// Transforms the attachment's four vertices to world coordinates. + /// @param bone The parent bone. + /// @param worldVertices The output world vertices. Must have a length greater than or equal to offset + 8. + /// @param offset The worldVertices index to begin writing values. + /// @param stride The number of worldVertices entries between the value pairs written. + void computeWorldVertices(Bone& bone, Vector& worldVertices, int offset, int stride = 2); + + float getX(); + void setX(float inValue); + float getY(); + void setY(float inValue); + float getRotation(); + void setRotation(float inValue); + float getScaleX(); + void setScaleX(float inValue); + float getScaleY(); + void setScaleY(float inValue); + float getWidth(); + void setWidth(float inValue); + float getHeight(); + void setHeight(float inValue); + + float getR(); + void setR(float inValue); + float getG(); + void setG(float inValue); + float getB(); + void setB(float inValue); + float getA(); + void setA(float inValue); + + std::string getPath(); + void setPath(std::string inValue); + void* getRendererObject(); + void setRendererObject(void* inValue); + float getRegionOffsetX(); + void setRegionOffsetX(float inValue); + + // Pixels stripped from the bottom left, unrotated. + float getRegionOffsetY(); + void setRegionOffsetY(float inValue); + float getRegionWidth(); + void setRegionWidth(float inValue); + + // Unrotated, stripped size. + float getRegionHeight(); + void setRegionHeight(float inValue); + float getRegionOriginalWidth(); + void setRegionOriginalWidth(float inValue); + + // Unrotated, unstripped size. + float getRegionOriginalHeight(); + void setRegionOriginalHeight(float inValue); + + Vector& getOffset(); + Vector& getUVs(); + + private: + static const int BLX; + static const int BLY; + static const int ULX; + static const int ULY; + static const int URX; + static const int URY; + static const int BRX; + static const int BRY; + + float _x, _y, _rotation, _scaleX, _scaleY, _width, _height; + float _regionOffsetX, _regionOffsetY, _regionWidth, _regionHeight, _regionOriginalWidth, _regionOriginalHeight; + Vector _offset; + Vector _uvs; + void* _rendererObject; + std::string _path; + float _regionU; + float _regionV; + float _regionU2; + float _regionV2; + float _r, _g, _b, _a; + }; +} + +#endif /* Spine_RegionAttachment_h */ diff --git a/spine-cpp/spine-cpp/include/spine/RotateMode.h b/spine-cpp/spine-cpp/include/spine/RotateMode.h new file mode 100644 index 000000000..f7dfab14d --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/RotateMode.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_RotateMode_h +#define Spine_RotateMode_h + +namespace Spine { + enum RotateMode { + RotateMode_Tangent = 0, + RotateMode_Chain, + RotateMode_ChainScale + }; +} + +#endif /* Spine_RotateMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/RotateTimeline.h b/spine-cpp/spine-cpp/include/spine/RotateTimeline.h new file mode 100644 index 000000000..558f5a96b --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/RotateTimeline.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_RotateTimeline_h +#define Spine_RotateTimeline_h + +#include + +namespace Spine { + class RotateTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class AnimationState; + + RTTI_DECL; + + public: + static const int ENTRIES = 2; + + RotateTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float degrees); + + int getBoneIndex(); + void setBoneIndex(int inValue); + + Vector& getFrames(); + void setFrames(Vector inValue); + + private: + static const int PREV_TIME = -2; + static const int PREV_ROTATION = -1; + static const int ROTATION = 1; + + int _boneIndex; + Vector _frames; // time, angle, ... + }; +} + +#endif /* Spine_RotateTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/ScaleTimeline.h b/spine-cpp/spine-cpp/include/spine/ScaleTimeline.h new file mode 100644 index 000000000..32f434287 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/ScaleTimeline.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ScaleTimeline_h +#define Spine_ScaleTimeline_h + +#include + +namespace Spine { + class ScaleTimeline : public TranslateTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + ScaleTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + }; +} + +#endif /* Spine_ScaleTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/ShearTimeline.h b/spine-cpp/spine-cpp/include/spine/ShearTimeline.h new file mode 100644 index 000000000..fa687e15d --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/ShearTimeline.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_ShearTimeline_h +#define Spine_ShearTimeline_h + +#include + +namespace Spine { + class ShearTimeline : public TranslateTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + ShearTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + }; +} + +#endif /* Spine_ShearTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Skeleton.h b/spine-cpp/spine-cpp/include/spine/Skeleton.h new file mode 100644 index 000000000..8efd439a3 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Skeleton.h @@ -0,0 +1,207 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Skeleton_h +#define Spine_Skeleton_h + +#include +#include + +#include +#include // std::numeric_limits + +namespace Spine { + class SkeletonData; + class Bone; + class Updatable; + class Slot; + class IkConstraint; + class PathConstraint; + class TransformConstraint; + class Skin; + class Attachment; + + class Skeleton { + friend class AnimationState; + friend class SkeletonBounds; + friend class SkeletonClipping; + + friend class AttachmentTimeline; + friend class ColorTimeline; + friend class DeformTimeline; + friend class DrawOrderTimeline; + friend class EventTimeline; + friend class IkConstraintTimeline; + friend class PathConstraintMixTimeline; + friend class PathConstraintPositionTimeline; + friend class PathConstraintSpacingTimeline; + friend class ScaleTimeline; + friend class ShearTimeline; + friend class TransformConstraintTimeline; + friend class TranslateTimeline; + friend class TwoColorTimeline; + + public: + Skeleton(SkeletonData& skeletonData); + + ~Skeleton(); + + /// Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added + /// or removed. + void updateCache(); + + /// Updates the world transform for each bone and applies constraints. + void updateWorldTransform(); + + /// Sets the bones, constraints, and slots to their setup pose values. + void setToSetupPose(); + + /// Sets the bones and constraints to their setup pose values. + void setBonesToSetupPose(); + + void setSlotsToSetupPose(); + + /// @return May be NULL. + Bone* findBone(std::string boneName); + + /// @return -1 if the bone was not found. + int findBoneIndex(std::string boneName); + + /// @return May be NULL. + Slot* findSlot(std::string slotName); + + /// @return -1 if the bone was not found. + int findSlotIndex(std::string slotName); + + /// Sets a skin by name (see setSkin). + void setSkin(std::string skinName); + + /// Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. + /// If there was no old skin, each slot's setup mode attachment is attached from the new skin. + /// After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling + /// See Skeleton::setSlotsToSetupPose() + /// Also, often AnimationState::apply(Skeleton&) is called before the next time the + /// skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin. + /// + /// @param newSkin May be NULL. + void setSkin(Skin* newSkin); + + /// @return May be NULL. + Attachment* getAttachment(std::string slotName, std::string attachmentName); + + /// @return May be NULL. + Attachment* getAttachment(int slotIndex, std::string attachmentName); + + /// @param attachmentName May be empty. + void setAttachment(std::string slotName, std::string attachmentName); + + /// @return May be NULL. + IkConstraint* findIkConstraint(std::string constraintName); + + /// @return May be NULL. + TransformConstraint* findTransformConstraint(std::string constraintName); + + /// @return May be NULL. + PathConstraint* findPathConstraint(std::string constraintName); + + void update(float delta); + + /// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose. + /// @param outX The horizontal distance between the skeleton origin and the left side of the AABB. + /// @param outY The vertical distance between the skeleton origin and the bottom side of the AABB. + /// @param outWidth The width of the AABB + /// @param outHeight The height of the AABB. + /// @param outVertexBuffer Reference to hold a Vector of floats. This method will assign it with new floats as needed. + void getBounds(float& outX, float& outY, float& outWidth, float& outHeight, Vector& outVertexBuffer); + + Bone* getRootBone(); + + const SkeletonData& getData(); + Vector& getBones(); + Vector& getUpdateCacheList(); + Vector& getSlots(); + Vector& getDrawOrder(); + Vector& getIkConstraints(); + Vector& getPathConstraints(); + Vector& getTransformConstraints(); + + Skin* getSkin(); + float getR(); + void setR(float inValue); + float getG(); + void setG(float inValue); + float getB(); + void setB(float inValue); + float getA(); + void setA(float inValue); + float getTime(); + void setTime(float inValue); + float getX(); + void setX(float inValue); + float getY(); + void setY(float inValue); + bool getFlipX(); + void setFlipX(float inValue); + bool getFlipY(); + void setFlipY(float inValue); + + private: + SkeletonData& _data; + Vector _bones; + Vector _slots; + Vector _drawOrder; + Vector _ikConstraints; + Vector _transformConstraints; + Vector _pathConstraints; + Vector _updateCache; + Vector _updateCacheReset; + Skin* _skin; + float _r, _g, _b, _a; + float _time; + bool _flipX, _flipY; + float _x, _y; + + void sortIkConstraint(IkConstraint* constraint); + + void sortPathConstraint(PathConstraint* constraint); + + void sortTransformConstraint(TransformConstraint* constraint); + + void sortPathConstraintAttachment(Skin* skin, int slotIndex, Bone& slotBone); + + void sortPathConstraintAttachment(Attachment* attachment, Bone& slotBone); + + void sortBone(Bone* bone); + + static void sortReset(Vector& bones); + }; +} + +#endif /* Spine_Skeleton_h */ diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonBinary.h b/spine-cpp/spine-cpp/include/spine/SkeletonBinary.h new file mode 100644 index 000000000..17749fca4 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SkeletonBinary.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_SkeletonBinary_h +#define Spine_SkeletonBinary_h + +#include +#include + +#include + +namespace Spine { + class SkeletonData; + class Atlas; + class AttachmentLoader; + class LinkedMesh; + class Skin; + class Attachment; + class VertexAttachment; + class Animation; + class CurveTimeline; + + class SkeletonBinary { + public: + static const int BONE_ROTATE; + static const int BONE_TRANSLATE; + static const int BONE_SCALE; + static const int BONE_SHEAR; + + static const int SLOT_ATTACHMENT; + static const int SLOT_COLOR; + static const int SLOT_TWO_COLOR; + + static const int PATH_POSITION; + static const int PATH_SPACING; + static const int PATH_MIX; + + static const int CURVE_LINEAR; + static const int CURVE_STEPPED; + static const int CURVE_BEZIER; + + static const TransformMode TRANSFORM_MODE_VALUES[5]; + + SkeletonBinary(Vector& atlasArray); + + SkeletonBinary(AttachmentLoader* attachmentLoader); + + ~SkeletonBinary(); + + SkeletonData* readSkeletonData(const unsigned char* binary, const int length); + + SkeletonData* readSkeletonDataFile(const char* path); + + private: + struct DataInput { + const unsigned char* cursor; + const unsigned char* end; + }; + + AttachmentLoader* _attachmentLoader; + Vector _linkedMeshes; + std::string _error; + float _scale; + const bool _ownsLoader; + + void setError(const char* value1, const char* value2); + + char* readString(DataInput* input); + + float readFloat(DataInput* input); + + unsigned char readByte(DataInput* input); + + signed char readSByte(DataInput* input); + + int readBoolean(DataInput* input); + + int readInt(DataInput* input); + + void readColor(DataInput* input, float *r, float *g, float *b, float *a); + + int readVarint(DataInput* input, bool optimizePositive); + + Skin* readSkin(DataInput* input, const char* skinName, SkeletonData* skeletonData, bool nonessential); + + Attachment* readAttachment(DataInput* input, Skin* skin, int slotIndex, const char* attachmentName, SkeletonData* skeletonData, bool nonessential); + + void readVertices(DataInput* input, VertexAttachment* attachment, int vertexCount); + + Vector readFloatArray(DataInput *input, int n, float scale); + + Vector readShortArray(DataInput *input); + + Animation* readAnimation(const char* name, DataInput* input, SkeletonData *skeletonData); + + void readCurve(DataInput* input, int frameIndex, CurveTimeline* timeline); + }; +} + +#endif /* Spine_SkeletonBinary_h */ diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonBounds.h b/spine-cpp/spine-cpp/include/spine/SkeletonBounds.h new file mode 100644 index 000000000..983600835 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SkeletonBounds.h @@ -0,0 +1,108 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_SkeletonBounds_h +#define Spine_SkeletonBounds_h + +#include + +namespace Spine { + class Skeleton; + class BoundingBoxAttachment; + + class Polygon; + + /// + /// Collects each BoundingBoxAttachment that is visible and computes the world vertices for its polygon. + /// The polygon vertices are provided along with convenience methods for doing hit detection. + /// + class SkeletonBounds { + public: + SkeletonBounds(); + + /// + /// Clears any previous polygons, finds all visible bounding box attachments, + /// and computes the world vertices for each bounding box's polygon. + /// @param skeleton The skeleton. + /// @param updateAabb + /// If true, the axis aligned bounding box containing all the polygons is computed. + /// If false, the SkeletonBounds AABB methods will always return true. + /// + void update(Skeleton& skeleton, bool updateAabb); + + /// Returns true if the axis aligned bounding box contains the point. + bool aabbcontainsPoint(float x, float y); + + /// Returns true if the axis aligned bounding box intersects the line segment. + bool aabbintersectsSegment(float x1, float y1, float x2, float y2); + + /// Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. + bool aabbIntersectsSkeleton(SkeletonBounds bounds); + + /// Returns true if the polygon contains the point. + bool containsPoint(Polygon* polygon, float x, float y); + + /// Returns the first bounding box attachment that contains the point, or NULL. When doing many checks, it is usually more + /// efficient to only call this method if {@link #aabbcontainsPoint(float, float)} returns true. + BoundingBoxAttachment* containsPoint(float x, float y); + + /// Returns the first bounding box attachment that contains the line segment, or NULL. When doing many checks, it is usually + /// more efficient to only call this method if {@link #aabbintersectsSegment(float, float, float, float)} returns true. + BoundingBoxAttachment* intersectsSegment(float x1, float y1, float x2, float y2); + + /// Returns true if the polygon contains the line segment. + bool intersectsSegment(Polygon* polygon, float x1, float y1, float x2, float y2); + + Polygon* getPolygon(BoundingBoxAttachment* attachment); + + float getWidth(); + float getHeight(); + + private: + Vector _polygonPool; + Vector _boundingBoxes; + Vector _polygons; + float _minX, _minY, _maxX, _maxY; + + void aabbCompute(); + }; + + class Polygon { + public: + Vector _vertices; + int _count; + + Polygon() : _count(0) { + _vertices.reserve(16); + } + }; +} + +#endif /* Spine_SkeletonBounds_h */ diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonClipping.h b/spine-cpp/spine-cpp/include/spine/SkeletonClipping.h new file mode 100644 index 000000000..6d5667517 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SkeletonClipping.h @@ -0,0 +1,78 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_SkeletonClipping_h +#define Spine_SkeletonClipping_h + +#include +#include + +namespace Spine { + class Slot; + class ClippingAttachment; + + class SkeletonClipping { + public: + SkeletonClipping(); + + int clipStart(Slot& slot, ClippingAttachment* clip); + + void clipEnd(Slot& slot); + + void clipEnd(); + + void clipTriangles(Vector& vertices, int verticesLength, Vector& triangles, int trianglesLength, Vector& uvs); + + bool isClipping(); + + Vector& getClippedVertices(); + Vector& getClippedTriangles(); + Vector& getClippedUVs(); + + private: + Triangulator _triangulator; + Vector _clippingPolygon; + Vector _clipOutput; + Vector _clippedVertices; + Vector _clippedTriangles; + Vector _clippedUVs; + Vector _scratch; + ClippingAttachment* _clipAttachment; + Vector< Vector* > _clippingPolygons; + + /** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping + * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */ + bool clip(float x1, float y1, float x2, float y2, float x3, float y3, Vector& clippingArea, Vector& output); + + static void makeClockwise(Vector& polygon); + }; +} + +#endif /* Spine_SkeletonClipping_h */ diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonData.h b/spine-cpp/spine-cpp/include/spine/SkeletonData.h new file mode 100644 index 000000000..53065ade0 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SkeletonData.h @@ -0,0 +1,162 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_SkeletonData_h +#define Spine_SkeletonData_h + +#include + +#include + +namespace Spine { + class BoneData; + class SlotData; + class Skin; + class EventData; + class Animation; + class IkConstraintData; + class TransformConstraintData; + class PathConstraintData; + + /// Stores the setup pose and all of the stateless data for a skeleton. + class SkeletonData { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class Skeleton; + + public: + SkeletonData(); + + ~SkeletonData(); + + /// Finds a bone by comparing each bone's name. + /// It is more efficient to cache the results of this method than to call it multiple times. + /// @return May be NULL. + BoneData* findBone(std::string boneName); + + /// @return -1 if the bone was not found. + int findBoneIndex(std::string boneName); + + /// @return May be NULL. + SlotData* findSlot(std::string slotName); + + /// @return -1 if the slot was not found. + int findSlotIndex(std::string slotName); + + /// @return May be NULL. + Skin* findSkin(std::string skinName); + + /// @return May be NULL. + EventData* findEvent(std::string eventDataName); + + /// @return May be NULL. + Animation* findAnimation(std::string animationName); + + /// @return May be NULL. + IkConstraintData* findIkConstraint(std::string constraintName); + + /// @return May be NULL. + TransformConstraintData* findTransformConstraint(std::string constraintName); + + /// @return May be NULL. + PathConstraintData* findPathConstraint(std::string constraintName); + + /// @return -1 if the path constraint was not found. + int findPathConstraintIndex(std::string pathConstraintName); + + std::string getName(); + void setName(std::string inValue); + + /// The skeleton's bones, sorted parent first. The root bone is always the first bone. + Vector& getBones(); + + Vector& getSlots(); + + /// All skins, including the default skin. + Vector& getSkins(); + void setSkins(Vector& inValue); + + /// The skeleton's default skin. + /// By default this skin contains all attachments that were not in a skin in Spine. + /// + /// @return May be NULL. + Skin* getDefaultSkin(); + void setDefaultSkin(Skin* inValue); + + Vector& getEvents(); + void setEvents(Vector& inValue); + Vector& getAnimations(); + void setAnimations(Vector& inValue); + Vector& getIkConstraints(); + void setIkConstraints(Vector& inValue); + Vector& getTransformConstraints(); + void setTransformConstraints(Vector& inValue); + Vector& getPathConstraints(); + void setPathConstraints(Vector& inValue); + + float getWidth(); + void setWidth(float inValue); + float getHeight(); + void setHeight(float inValue); + + /// The Spine version used to export this data, or NULL. + std::string getVersion(); + void setVersion(std::string inValue); + std::string getHash(); + void setHash(std::string inValue); + std::string getImagesPath(); + void setImagesPath(std::string inValue); + + /// The dopesheet FPS in Spine. Available only when nonessential data was exported. + float getFps(); + void setFps(float inValue); + + private: + std::string _name; + Vector _bones; // Ordered parents first + Vector _slots; // Setup pose draw order. + Vector _skins; + Skin* _defaultSkin; + Vector _events; + Vector _animations; + Vector _ikConstraints; + Vector _transformConstraints; + Vector _pathConstraints; + float _width, _height; + std::string _version; + std::string _hash; + + // Nonessential. + float _fps; + std::string _imagesPath; + }; +} + +#endif /* Spine_SkeletonData_h */ diff --git a/spine-cpp/spine-cpp/include/spine/SkeletonJson.h b/spine-cpp/spine-cpp/include/spine/SkeletonJson.h new file mode 100644 index 000000000..ef40a7a83 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SkeletonJson.h @@ -0,0 +1,79 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_SkeletonJson_h +#define Spine_SkeletonJson_h + +#include + +#include + +namespace Spine { + class CurveTimeline; + class VertexAttachment; + class Animation; + class Json; + class SkeletonData; + class Atlas; + class AttachmentLoader; + class LinkedMesh; + + class SkeletonJson { + public: + SkeletonJson(Vector& atlasArray); + + SkeletonJson(AttachmentLoader* attachmentLoader); + + ~SkeletonJson(); + + SkeletonData* readSkeletonDataFile(const char* path); + + SkeletonData* readSkeletonData(const char* json); + + private: + AttachmentLoader* _attachmentLoader; + Vector _linkedMeshes; + float _scale; + const bool _ownsLoader; + std::string _error; + + static float toColor(const char* value, int index); + + static void readCurve(Json* frame, CurveTimeline* timeline, int frameIndex); + + Animation* readAnimation(Json* root, SkeletonData *skeletonData); + + void readVertices(Json* attachmentMap, VertexAttachment* attachment, int verticesLength); + + void setError(Json* root, const char* value1, const char* value2); + }; +} + +#endif /* Spine_SkeletonJson_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Skin.h b/spine-cpp/spine-cpp/include/spine/Skin.h new file mode 100644 index 000000000..5756b65d3 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Skin.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Skin_h +#define Spine_Skin_h + +#include +#include +#include + +namespace Spine { + class Attachment; + class Skeleton; + + /// Stores attachments by slot index and attachment name. + /// See SkeletonData::getDefaultSkin, Skeleton::getSkin, and + /// http://esotericsoftware.com/spine-runtime-skins in the Spine Runtimes Guide. + class Skin { + friend class Skeleton; + + public: + class AttachmentKey { + public: + int _slotIndex; + std::string _name; + + AttachmentKey(int slotIndex = 0, std::string name = ""); + + bool operator==(const AttachmentKey &other) const; + }; + + struct HashAttachmentKey { + std::size_t operator()(const Spine::Skin::AttachmentKey& val) const; + }; + + Skin(std::string name); + + /// Adds an attachment to the skin for the specified slot index and name. + /// If the name already exists for the slot, the previous value is replaced. + void addAttachment(int slotIndex, std::string name, Attachment* attachment); + + /// Returns the attachment for the specified slot index and name, or NULL. + Attachment* getAttachment(int slotIndex, std::string name); + + /// Finds the skin keys for a given slot. The results are added to the passed array of names. + /// @param slotIndex The target slotIndex. To find the slot index, use Skeleton::findSlotIndex or SkeletonData::findSlotIndex + /// @param names Found skin key names will be added to this array. + void findNamesForSlot(int slotIndex, Vector& names); + + /// Finds the attachments for a given slot. The results are added to the passed array of Attachments. + /// @param slotIndex The target slotIndex. To find the slot index, use Skeleton::findSlotIndex or SkeletonData::findSlotIndex + /// @param attachments Found Attachments will be added to this array. + void findAttachmentsForSlot(int slotIndex, Vector& attachments); + + const std::string& getName(); + HashMap& getAttachments(); + + private: + const std::string _name; + HashMap _attachments; + + /// Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached. + void attachAll(Skeleton& skeleton, Skin& oldSkin); + }; +} + +#endif /* Spine_Skin_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Slot.h b/spine-cpp/spine-cpp/include/spine/Slot.h new file mode 100644 index 000000000..69f7f6666 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Slot.h @@ -0,0 +1,115 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Slot_h +#define Spine_Slot_h + +#include + +#include + +namespace Spine { + class SlotData; + class Bone; + class Skeleton; + class Attachment; + + class Slot { + friend class VertexAttachment; + friend class Skeleton; + friend class SkeletonBounds; + friend class SkeletonClipping; + + friend class AttachmentTimeline; + friend class ColorTimeline; + friend class DeformTimeline; + friend class DrawOrderTimeline; + friend class EventTimeline; + friend class IkConstraintTimeline; + friend class PathConstraintMixTimeline; + friend class PathConstraintPositionTimeline; + friend class PathConstraintSpacingTimeline; + friend class ScaleTimeline; + friend class ShearTimeline; + friend class TransformConstraintTimeline; + friend class TranslateTimeline; + friend class TwoColorTimeline; + + public: + Slot(SlotData& data, Bone& bone); + + void setToSetupPose(); + + SlotData& getData(); + Bone& getBone(); + Skeleton& getSkeleton(); + + float getR(); + void setR(float inValue); + float getG(); + void setG(float inValue); + float getB(); + void setB(float inValue); + float getA(); + void setA(float inValue); + + float getR2(); + void setR2(float inValue); + float getG2(); + void setG2(float inValue); + float getB2(); + void setB2(float inValue); + bool hasSecondColor(); + void setHasSecondColor(bool inValue); + + /// May be NULL. + Attachment* getAttachment(); + void setAttachment(Attachment* inValue); + + float getAttachmentTime(); + void setAttachmentTime(float inValue); + + Vector& getAttachmentVertices(); + void setAttachmentVertices(Vector inValue); + + private: + SlotData& _data; + Bone& _bone; + Skeleton& _skeleton; + float _r, _g, _b, _a; + float _r2, _g2, _b2; + bool _hasSecondColor; + Attachment* _attachment; + float _attachmentTime; + Vector _attachmentVertices; + }; +} + +#endif /* Spine_Slot_h */ diff --git a/spine-cpp/spine-cpp/include/spine/SlotData.h b/spine-cpp/spine-cpp/include/spine/SlotData.h new file mode 100644 index 000000000..6e422e534 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SlotData.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_SlotData_h +#define Spine_SlotData_h + +#include + +#include + +namespace Spine { + class BoneData; + + class SlotData { + friend class SkeletonBinary; + friend class SkeletonJson; + + friend class AttachmentTimeline; + friend class ColorTimeline; + friend class DeformTimeline; + friend class DrawOrderTimeline; + friend class EventTimeline; + friend class IkConstraintTimeline; + friend class PathConstraintMixTimeline; + friend class PathConstraintPositionTimeline; + friend class PathConstraintSpacingTimeline; + friend class ScaleTimeline; + friend class ShearTimeline; + friend class TransformConstraintTimeline; + friend class TranslateTimeline; + friend class TwoColorTimeline; + + public: + SlotData(int index, std::string name, BoneData& boneData); + + const int getIndex(); + + const std::string& getName(); + + BoneData& getBoneData(); + + float getR(); + void setR(float inValue); + float getG(); + void setG(float inValue); + float getB(); + void setB(float inValue); + float getA(); + void setA(float inValue); + + float getR2(); + void setR2(float inValue); + float getG2(); + void setG2(float inValue); + float getB2(); + void setB2(float inValue); + bool hasSecondColor(); + void setHasSecondColor(bool inValue); + + /// May be empty. + std::string getAttachmentName(); + void setAttachmentName(std::string inValue); + + BlendMode getBlendMode(); + void setBlendMode(BlendMode inValue); + + private: + const int _index; + const std::string _name; + BoneData& _boneData; + float _r, _g, _b, _a; + float _r2, _g2, _b2, _a2; + bool _hasSecondColor; + std::string _attachmentName; + BlendMode _blendMode; + }; +} + +#endif /* Spine_SlotData_h */ diff --git a/spine-cpp/spine-cpp/include/spine/SpacingMode.h b/spine-cpp/spine-cpp/include/spine/SpacingMode.h new file mode 100644 index 000000000..b8bf965af --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/SpacingMode.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_SpacingMode_h +#define Spine_SpacingMode_h + +namespace Spine { + enum SpacingMode { + SpacingMode_Length = 0, + SpacingMode_Fixed, + SpacingMode_Percent + }; +} + +#endif /* Spine_SpacingMode_h */ diff --git a/spine-cpp/spine-cpp/include/spine/TextureLoader.h b/spine-cpp/spine-cpp/include/spine/TextureLoader.h new file mode 100644 index 000000000..f0b836910 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/TextureLoader.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TextureLoader_h +#define Spine_TextureLoader_h + +#include + +namespace Spine { + class AtlasPage; + + class TextureLoader { + public: + TextureLoader(); + + virtual ~TextureLoader(); + + virtual void load(AtlasPage& page, std::string path) = 0; + + virtual void unload(void* texture) = 0; + }; +} + +#endif /* Spine_TextureLoader_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Timeline.h b/spine-cpp/spine-cpp/include/spine/Timeline.h new file mode 100644 index 000000000..d23a36753 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Timeline.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Timeline_h +#define Spine_Timeline_h + +#include +#include +#include +#include + +namespace Spine { + class Skeleton; + class Event; + + class Timeline { + RTTI_DECL; + + public: + Timeline(); + + virtual ~Timeline(); + + /// Sets the value(s) for the specified time. + /// @param skeleton The skeleton the timeline is being applied to. This provides access to the bones, slots, and other skeleton components the timeline may change. + /// @param lastTime lastTime The time this timeline was last applied. Timelines such as EventTimeline trigger only at specific times rather than every frame. In that case, the timeline triggers everything between lastTime (exclusive) and time (inclusive). + /// @param time The time within the animation. Most timelines find the key before and the key after this time so they can interpolate between the keys. + /// @param pEvents If any events are fired, they are added to this array. Can be NULL to ignore firing events or if the timeline does not fire events. May be NULL. + /// @param alpha alpha 0 applies the current or setup pose value (depending on pose parameter). 1 applies the timeline + /// value. Between 0 and 1 applies a value between the current or setup pose and the timeline value. By adjusting + /// alpha over time, an animation can be mixed in or out. alpha can also be useful to + /// apply animations on top of each other (layered). + /// @param pose Controls how mixing is applied when alpha is than 1. + /// @param direction Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions such as DrawOrderTimeline and AttachmentTimeline. + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) = 0; + + virtual int getPropertyId() = 0; + }; +} + +#endif /* Spine_Timeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/TimelineType.h b/spine-cpp/spine-cpp/include/spine/TimelineType.h new file mode 100644 index 000000000..518c2ce63 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/TimelineType.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TimelineType_h +#define Spine_TimelineType_h + +namespace Spine { + enum TimelineType { + TimelineType_Rotate = 0, + TimelineType_Translate, + TimelineType_Scale, + TimelineType_Shear, + TimelineType_Attachment, + TimelineType_Color, + TimelineType_Deform, + TimelineType_Event, + TimelineType_DrawOrder, + TimelineType_IkConstraint, + TimelineType_TransformConstraint, + TimelineType_PathConstraintPosition, + TimelineType_PathConstraintSpacing, + TimelineType_PathConstraintMix, + TimelineType_TwoColor + }; +} + +#endif /* Spine_TimelineType_h */ diff --git a/spine-cpp/spine-cpp/include/spine/TransformConstraint.h b/spine-cpp/spine-cpp/include/spine/TransformConstraint.h new file mode 100644 index 000000000..b878c0f4c --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/TransformConstraint.h @@ -0,0 +1,93 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TransformConstraint_h +#define Spine_TransformConstraint_h + +#include + +#include + +namespace Spine { + class TransformConstraintData; + class Skeleton; + class Bone; + + class TransformConstraint : public Constraint { + friend class Skeleton; + friend class TransformConstraintTimeline; + + RTTI_DECL; + + public: + TransformConstraint(TransformConstraintData& data, Skeleton& skeleton); + + void apply(); + + virtual void update(); + + virtual int getOrder(); + + TransformConstraintData& getData(); + + Vector& getBones(); + + Bone* getTarget(); + void setTarget(Bone* inValue); + + float getRotateMix(); + void setRotateMix(float inValue); + + float getTranslateMix(); + void setTranslateMix(float inValue); + + float getScaleMix(); + void setScaleMix(float inValue); + + float getShearMix(); + void setShearMix(float inValue); + + private: + TransformConstraintData& _data; + Vector _bones; + Bone* _target; + float _rotateMix, _translateMix, _scaleMix, _shearMix; + + void applyAbsoluteWorld(); + + void applyRelativeWorld(); + + void applyAbsoluteLocal(); + + void applyRelativeLocal(); + }; +} + +#endif /* Spine_TransformConstraint_h */ diff --git a/spine-cpp/spine-cpp/include/spine/TransformConstraintData.h b/spine-cpp/spine-cpp/include/spine/TransformConstraintData.h new file mode 100644 index 000000000..090675a31 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/TransformConstraintData.h @@ -0,0 +1,82 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TransformConstraintData_h +#define Spine_TransformConstraintData_h + +#include + +#include + +namespace Spine { + class BoneData; + + class TransformConstraintData { + friend class SkeletonBinary; + friend class SkeletonJson; + + friend class TransformConstraint; + friend class Skeleton; + friend class TransformConstraintTimeline; + + public: + TransformConstraintData(std::string name); + + const std::string& getName(); + int getOrder(); + Vector& getBones(); + BoneData* getTarget(); + float getRotateMix(); + float getTranslateMix(); + float getScaleMix(); + float getShearMix(); + + float getOffsetRotation(); + float getOffsetX(); + float getOffsetY(); + float getOffsetScaleX(); + float getOffsetScaleY(); + float getOffsetShearY(); + + bool isRelative(); + bool isLocal(); + + private: + const std::string _name; + int _order; + Vector _bones; + BoneData* _target; + float _rotateMix, _translateMix, _scaleMix, _shearMix; + float _offsetRotation, _offsetX, _offsetY, _offsetScaleX, _offsetScaleY, _offsetShearY; + bool _relative, _local; + }; +} + +#endif /* Spine_TransformConstraintData_h */ diff --git a/spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h b/spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h new file mode 100644 index 000000000..f117bbf82 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TransformConstraintTimeline_h +#define Spine_TransformConstraintTimeline_h + +#include + +namespace Spine { + class TransformConstraintTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + static const int ENTRIES; + + TransformConstraintTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + void setFrame(int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix); + + private: + static const int PREV_TIME, PREV_ROTATE, PREV_TRANSLATE, PREV_SCALE, PREV_SHEAR; + static const int ROTATE, TRANSLATE, SCALE, SHEAR; + + Vector _frames; + int _transformConstraintIndex; + }; +} + +#endif /* Spine_TransformConstraintTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/TransformMode.h b/spine-cpp/spine-cpp/include/spine/TransformMode.h new file mode 100644 index 000000000..d3cb2b7f9 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/TransformMode.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TransformMode_h +#define Spine_TransformMode_h + +namespace Spine { + enum TransformMode { + //0000 0 Flip Scale Rotation + TransformMode_Normal = 0, // 0000 + TransformMode_OnlyTranslation = 7, // 0111 + TransformMode_NoRotationOrReflection = 1, // 0001 + TransformMode_NoScale = 2, // 0010 + TransformMode_NoScaleOrReflection = 6, // 0110 + }; +} + +#endif /* Spine_TransformMode_h */ + diff --git a/spine-cpp/spine-cpp/include/spine/TranslateTimeline.h b/spine-cpp/spine-cpp/include/spine/TranslateTimeline.h new file mode 100644 index 000000000..ae1f209d7 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/TranslateTimeline.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TranslateTimeline_h +#define Spine_TranslateTimeline_h + +#include + +#include +#include + +namespace Spine { + class TranslateTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + static const int ENTRIES; + + TranslateTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float x, float y); + + protected: + static const int PREV_TIME, PREV_X, PREV_Y; + static const int X, Y; + + Vector _frames; + int _boneIndex; + }; +} + +#endif /* Spine_TranslateTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Triangulator.h b/spine-cpp/spine-cpp/include/spine/Triangulator.h new file mode 100644 index 000000000..d5fff1994 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Triangulator.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Triangulator_h +#define Spine_Triangulator_h + +#include +#include + +namespace Spine { + class Triangulator { + public: + Vector& triangulate(Vector& vertices); + + Vector< Vector* > decompose(Vector& vertices, Vector& triangles); + + private: + Vector< Vector* > _convexPolygons; + Vector< Vector* > _convexPolygonsIndices; + + Vector _indices; + Vector _isConcaveArray; + Vector _triangles; + + Pool< Vector > _polygonPool; + Pool< Vector > _polygonIndicesPool; + + static bool isConcave(int index, int vertexCount, Vector& vertices, Vector& indices); + + static bool positiveArea(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y); + + static int winding(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y); + }; +} + +#endif /* Spine_Triangulator_h */ diff --git a/spine-cpp/spine-cpp/include/spine/TwoColorTimeline.h b/spine-cpp/spine-cpp/include/spine/TwoColorTimeline.h new file mode 100644 index 000000000..34977c3ec --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/TwoColorTimeline.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_TwoColorTimeline_h +#define Spine_TwoColorTimeline_h + +#include + +namespace Spine { + class TwoColorTimeline : public CurveTimeline { + friend class SkeletonBinary; + friend class SkeletonJson; + + RTTI_DECL; + + public: + static const int ENTRIES; + + TwoColorTimeline(int frameCount); + + virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction); + + virtual int getPropertyId(); + + /// Sets the time and value of the specified keyframe. + void setFrame(int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2); + + int getSlotIndex(); + void setSlotIndex(int inValue); + + private: + static const int PREV_TIME, PREV_R, PREV_G, PREV_B, PREV_A; + static const int PREV_R2, PREV_G2, PREV_B2; + static const int R, G, B, A, R2, G2, B2; + + Vector _frames; // time, r, g, b, a, r2, g2, b2, ... + int _slotIndex; + }; +} + +#endif /* Spine_TwoColorTimeline_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Updatable.h b/spine-cpp/spine-cpp/include/spine/Updatable.h new file mode 100644 index 000000000..d076e0439 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Updatable.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Updatable_h +#define Spine_Updatable_h + +#include + +namespace Spine { + class Updatable { + RTTI_DECL; + + public: + Updatable(); + + virtual ~Updatable(); + + virtual void update() = 0; + }; +} + +#endif /* Spine_Updatable_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Vector.h b/spine-cpp/spine-cpp/include/spine/Vector.h new file mode 100644 index 000000000..6fad04736 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Vector.h @@ -0,0 +1,231 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Vector_h +#define Spine_Vector_h + +#include + +#include +#include +#include + +namespace Spine { + template + class Vector { + public: + Vector() : _size(0), _capacity(0), _buffer(NULL) { + // Empty + } + + Vector(const Vector& inVector) : _size(inVector._size), _capacity(inVector._capacity), _buffer(NULL) { + if (_capacity > 0) { + _buffer = allocate(_capacity); + for (size_t i = 0; i < _size; ++i) { + construct(_buffer + i, inVector._buffer[i]); + } + } + } + + Vector& operator=(Vector& inVector) { + if (this != &inVector) { + clear(); + deallocate(_buffer); + _buffer = NULL; + + _size = inVector._size; + _capacity = inVector._capacity; + + if (_capacity > 0) { + _buffer = allocate(_capacity); + for (size_t i = 0; i < _size; ++i) { + construct(_buffer + i, inVector._buffer[i]); + } + } + } + + return *this; + } + + ~Vector() { + clear(); + deallocate(_buffer); + } + + bool contains(const T& inValue) { + for (size_t i = 0; i < _size; ++i) { + if (_buffer[i] == inValue) { + return true; + } + } + + return false; + } + + int indexOf(const T& inValue) { + for (size_t i = 0; i < _size; ++i) { + if (_buffer[i] == inValue) { + return static_cast(i); + } + } + + return -1; + } + + void push_back(const T& inValue) { + if (_size == _capacity) { + reserve(); + } + + construct(_buffer + _size++, inValue); + } + + void insert(size_t inIndex, const T& inValue) { + assert(inIndex < _size); + + if (_size == _capacity) { + reserve(); + } + + for (size_t i = ++_size - 1; i > inIndex; --i) { + construct(_buffer + i, _buffer[i - 1]); + destroy(_buffer + (i - 1)); + } + + construct(_buffer + inIndex, inValue); + } + + void erase(size_t inIndex) { + assert(inIndex < _size); + + --_size; + + if (inIndex != _size) { + for (size_t i = inIndex; i < _size; ++i) { + std::swap(_buffer[i], _buffer[i + 1]); + } + } + + destroy(_buffer + _size); + } + + void clear() { + for (size_t i = 0; i < _size; ++i) { + destroy(_buffer + (_size - 1 - i)); + } + + _size = 0; + } + + size_t size() const { + return _size; + } + + T& operator[](size_t inIndex) { + assert(inIndex < _size); + + return _buffer[inIndex]; + } + + void reserve(size_t inCapacity = 0) { + size_t newCapacity = inCapacity > 0 ? inCapacity : _capacity > 0 ? _capacity * 2 : 1; + if (newCapacity > _capacity) { + _buffer = REALLOC(_buffer, T, newCapacity); + _capacity = newCapacity; + } + } + + void setSize(size_t inValue) { + assert(inValue <= _capacity); + + _size = inValue; + } + + T* begin() { + return &_buffer[0]; + } + + T* end() { + return &_buffer[_size]; + } + + friend bool operator==(Vector& lhs, Vector& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + for (int i = 0, n = static_cast(lhs.size()); i < n; ++i) { + if (lhs[i] != rhs[i]) { + return false; + } + } + + return true; + } + + friend bool operator!=(Vector& lhs, Vector& rhs) { + return !(lhs == rhs); + } + + private: + size_t _size; + size_t _capacity; + T* _buffer; + + T* allocate(size_t n) { + assert(n > 0); + + T* ptr = MALLOC(T, n); + + assert(ptr); + + return ptr; + } + + void deallocate(T* buffer) { + if (_buffer) { + FREE(buffer); + } + } + + void construct(T* buffer, const T& val) { + /// This is a placement new operator + /// which basically means we are contructing a new object + /// using pre-allocated memory + new (buffer) T(val); + } + + void destroy(T* buffer) { + buffer->~T(); + } + }; +} + +#endif /* Spine_Vector_h */ diff --git a/spine-cpp/spine-cpp/include/spine/VertexAttachment.h b/spine-cpp/spine-cpp/include/spine/VertexAttachment.h new file mode 100644 index 000000000..4385886f5 --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/VertexAttachment.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_VertexAttachment_h +#define Spine_VertexAttachment_h + +#include + +#include + +namespace Spine { + class Slot; + + /// An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's vertices. + class VertexAttachment : public Attachment { + friend class SkeletonBinary; + friend class SkeletonJson; + friend class DeformTimeline; + + RTTI_DECL; + + public: + VertexAttachment(std::string name); + + void computeWorldVertices(Slot& slot, Vector& worldVertices); + + /// Transforms local vertices to world coordinates. + /// @param start The index of the first Vertices value to transform. Each vertex has 2 values, x and y. + /// @param count The number of world vertex values to output. Must be less than or equal to WorldVerticesLength - start. + /// @param worldVertices The output world vertices. Must have a length greater than or equal to offset + count. + /// @param offset The worldVertices index to begin writing values. + /// @param stride The number of worldVertices entries between the value pairs written. + void computeWorldVertices(Slot& slot, int start, int count, Vector& worldVertices, int offset, int stride = 2); + + /// @return true if a deform originally applied to the specified attachment should be applied to this attachment. + virtual bool applyDeform(VertexAttachment* sourceAttachment); + + /// Gets a unique ID for this attachment. + int getId(); + + Vector& getBones(); + void setBones(Vector inValue); + + Vector& getVertices(); + void setVertices(Vector inValue); + + int getWorldVerticesLength(); + void setWorldVerticesLength(int inValue); + + protected: + Vector _bones; + Vector _vertices; + int _worldVerticesLength; + + private: + const int _id; + + static int getNextID(); + }; +} + +#endif /* Spine_VertexAttachment_h */ diff --git a/spine-cpp/spine-cpp/include/spine/Vertices.h b/spine-cpp/spine-cpp/include/spine/Vertices.h new file mode 100644 index 000000000..0dff1321d --- /dev/null +++ b/spine-cpp/spine-cpp/include/spine/Vertices.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef Spine_Vertices_h +#define Spine_Vertices_h + +#include + +namespace Spine { + class Vertices { + public: + Vector _bones; + Vector _vertices; + }; +} + +#endif /* Spine_Vertices_h */ diff --git a/spine-cpp/spine-cpp/src/spine/Animation.cpp b/spine-cpp/spine-cpp/src/spine/Animation.cpp new file mode 100644 index 000000000..3b25f5b95 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Animation.cpp @@ -0,0 +1,146 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include +#include /* fmod */ + +namespace Spine { + Animation::Animation(std::string name, Vector& timelines, float duration) : + _timelines(timelines), + _duration(duration), + _name(name) { + assert(_name.length() > 0); + } + + Animation::~Animation() { + ContainerUtil::cleanUpVectorOfPointers(_timelines); + } + + void Animation::apply(Skeleton& skeleton, float lastTime, float time, bool loop, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + if (loop && _duration != 0) { + time = fmod(time, _duration); + if (lastTime > 0) { + lastTime = fmod(lastTime, _duration); + } + } + + for (int i = 0, n = static_cast(_timelines.size()); i < n; ++i) { + _timelines[i]->apply(skeleton, lastTime, time, pEvents, alpha, pose, direction); + } + } + + std::string Animation::getName() { + return _name; + } + + Vector Animation::getTimelines() { + return _timelines; + } + + void Animation::setTimelines(Vector inValue) { + _timelines = inValue; + } + + float Animation::getDuration() { + return _duration; + } + + void Animation::setDuration(float inValue) { + _duration = inValue; + } + + int Animation::binarySearch(Vector& values, float target, int step) { + int low = 0; + int size = static_cast(values.size()); + int high = size / step - 2; + if (high == 0) { + return step; + } + + int current = (int)(static_cast(high) >> 1); + while (true) { + if (values[(current + 1) * step] <= target) { + low = current + 1; + } + else { + high = current; + } + + if (low == high) { + return (low + 1) * step; + } + + current = (int)(static_cast(low + high) >> 1); + } + } + + int Animation::binarySearch(Vector& values, float target) { + int low = 0; + int size = static_cast(values.size()); + int high = size - 2; + if (high == 0) { + return 1; + } + + int current = (int)(static_cast(high) >> 1); + while (true) { + if (values[(current + 1)] <= target) { + low = current + 1; + } + else { + high = current; + } + + if (low == high) { + return (low + 1); + } + + current = (int)(static_cast(low + high) >> 1); + } + } + + int Animation::linearSearch(Vector& values, float target, int step) { + for (int i = 0, last = static_cast(values.size()) - step; i <= last; i += step) { + if (values[i] > target) { + return i; + } + } + + return -1; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/AnimationState.cpp b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp new file mode 100644 index 000000000..9d0140b82 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/AnimationState.cpp @@ -0,0 +1,975 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Spine { + void dummyOnAnimationEventFunc(AnimationState* state, EventType type, TrackEntry* entry, Event* event = NULL) { + // Empty + } + + TrackEntry::TrackEntry() : _animation(NULL), _next(NULL), _mixingFrom(NULL), _trackIndex(0), _loop(false), _eventThreshold(0), _attachmentThreshold(0), _drawOrderThreshold(0), _animationStart(0), _animationEnd(0), _animationLast(0), _nextAnimationLast(0), _delay(0), _trackTime(0), _trackLast(0), _nextTrackLast(0), _trackEnd(0), _timeScale(1.0f), _alpha(0), _mixTime(0), _mixDuration(0), _interruptAlpha(0), _totalAlpha(0), _onAnimationEventFunc(dummyOnAnimationEventFunc) { + // Empty + } + + int TrackEntry::getTrackIndex() { return _trackIndex; } + + Animation* TrackEntry::getAnimation() { return _animation; } + + bool TrackEntry::getLoop() { return _loop; } + void TrackEntry::setLoop(bool inValue) { _loop = inValue; } + + float TrackEntry::getDelay() { return _delay; } + void TrackEntry::setDelay(float inValue) { _delay = inValue; } + + float TrackEntry::getTrackTime() { return _trackTime; } + void TrackEntry::setTrackTime(float inValue) { _trackTime = inValue; } + + float TrackEntry::getTrackEnd() { return _trackEnd; } + void TrackEntry::setTrackEnd(float inValue) { _trackEnd = inValue; } + + float TrackEntry::getAnimationStart() { return _animationStart; } + void TrackEntry::setAnimationStart(float inValue) { _animationStart = inValue; } + + float TrackEntry::getAnimationEnd() { return _animationEnd; } + void TrackEntry::setAnimationEnd(float inValue) { _animationEnd = inValue; } + + float TrackEntry::getAnimationLast() { return _animationLast; } + void TrackEntry::setAnimationLast(float inValue) { + _animationLast = inValue; + _nextAnimationLast = inValue; + } + + float TrackEntry::getAnimationTime() { + if (_loop) { + float duration = _animationEnd - _animationStart; + if (duration == 0) { + return _animationStart; + } + + return fmodf(_trackTime, duration) + _animationStart; + } + + return MIN(_trackTime + _animationStart, _animationEnd); + } + + float TrackEntry::getTimeScale() { return _timeScale; } + void TrackEntry::setTimeScale(float inValue) { _timeScale = inValue; } + + float TrackEntry::getAlpha() { return _alpha; } + void TrackEntry::setAlpha(float inValue) { _alpha = inValue; } + + float TrackEntry::getEventThreshold() { return _eventThreshold; } + void TrackEntry::setEventThreshold(float inValue) { _eventThreshold = inValue; } + + float TrackEntry::getAttachmentThreshold() { return _attachmentThreshold; } + void TrackEntry::setAttachmentThreshold(float inValue) { _attachmentThreshold = inValue; } + + float TrackEntry::getDrawOrderThreshold() { return _drawOrderThreshold; } + void TrackEntry::setDrawOrderThreshold(float inValue) { _drawOrderThreshold = inValue; } + + TrackEntry* TrackEntry::getNext() { return _next; } + + bool TrackEntry::isComplete() { + return _trackTime >= _animationEnd - _animationStart; + } + + float TrackEntry::getMixTime() { return _mixTime; } + void TrackEntry::setMixTime(float inValue) { _mixTime = inValue; } + + float TrackEntry::getMixDuration() { return _mixDuration; } + void TrackEntry::setMixDuration(float inValue) { _mixDuration = inValue; } + + TrackEntry* TrackEntry::getMixingFrom() { return _mixingFrom; } + + void TrackEntry::resetRotationDirections() { + _timelinesRotation.clear(); + } + + void TrackEntry::setOnAnimationEventFunc(OnAnimationEventFunc inValue) { + _onAnimationEventFunc = inValue; + } + + TrackEntry* TrackEntry::setTimelineData(TrackEntry* to, Vector& mixingToArray, Vector& propertyIDs) { + if (to != NULL) { + mixingToArray.push_back(to); + } + + TrackEntry* lastEntry = _mixingFrom != NULL ? _mixingFrom->setTimelineData(this, mixingToArray, propertyIDs) : this; + + if (to != NULL) { + mixingToArray.erase(mixingToArray.size() - 1); + } + + int mixingToLast = static_cast(mixingToArray.size()) - 1; + Vector& timelines = _animation->_timelines; + int timelinesCount = static_cast(timelines.size()); + _timelineData.reserve(timelinesCount); + _timelineData.setSize(timelinesCount); + _timelineDipMix.clear(); + _timelineDipMix.reserve(timelinesCount); + _timelineDipMix.setSize(timelinesCount); + + // outer: + for (int i = 0; i < timelinesCount; ++i) { + int id = timelines[i]->getPropertyId(); + if (propertyIDs.contains(id)) { + _timelineData[i] = AnimationState::Subsequent; + } + else { + propertyIDs.push_back(id); + + if (to == NULL || !to->hasTimeline(id)) { + _timelineData[i] = AnimationState::First; + } + else { + for (int ii = mixingToLast; ii >= 0; --ii) { + TrackEntry* entry = mixingToArray[ii]; + if (!entry->hasTimeline(id)) { + if (entry->_mixDuration > 0) { + _timelineData[i] = AnimationState::DipMix; + _timelineDipMix[i] = entry; + goto continue_outer; // continue outer; + } + break; + } + } + _timelineData[i] = AnimationState::Dip; + } + } + continue_outer: {} + } + + return lastEntry; + } + + bool TrackEntry::hasTimeline(int inId) { + Vector& timelines = _animation->_timelines; + for (int i = 0, n = static_cast(timelines.size()); i < n; ++i) { + if (timelines[i]->getPropertyId() == inId) { + return true; + } + } + return false; + } + + void TrackEntry::reset() { + _animation = NULL; + _next = NULL; + _mixingFrom = NULL; + + _timelineData.clear(); + _timelineDipMix.clear(); + _timelinesRotation.clear(); + + _onAnimationEventFunc = dummyOnAnimationEventFunc; + } + + EventQueueEntry::EventQueueEntry(EventType eventType, TrackEntry* trackEntry, Event* event) : + _type(eventType), + _entry(trackEntry), + _event(event) { + // Empty + } + + EventQueue* EventQueue::newEventQueue(AnimationState& state, Pool& trackEntryPool) { + EventQueue* ret = NEW(EventQueue); + new (ret) EventQueue(state, trackEntryPool); + + return ret; + } + + EventQueueEntry* EventQueue::newEventQueueEntry(EventType eventType, TrackEntry* entry, Event* event) { + EventQueueEntry* ret = NEW(EventQueueEntry); + new (ret) EventQueueEntry(eventType, entry, event); + + return ret; + } + + EventQueue::EventQueue(AnimationState& state, Pool& trackEntryPool) : _state(state), _trackEntryPool(trackEntryPool), _drainDisabled(false) { + // Empty + } + + EventQueue::~EventQueue() { + ContainerUtil::cleanUpVectorOfPointers(_eventQueueEntries); + } + + void EventQueue::start(TrackEntry* entry) { + _eventQueueEntries.push_back(newEventQueueEntry(EventType_Start, entry)); + _state._animationsChanged = true; + } + + void EventQueue::interrupt(TrackEntry* entry) { + _eventQueueEntries.push_back(newEventQueueEntry(EventType_Interrupt, entry)); + } + + void EventQueue::end(TrackEntry* entry) { + _eventQueueEntries.push_back(newEventQueueEntry(EventType_End, entry)); + _state._animationsChanged = true; + } + + void EventQueue::dispose(TrackEntry* entry) { + _eventQueueEntries.push_back(newEventQueueEntry(EventType_Dispose, entry)); + } + + void EventQueue::complete(TrackEntry* entry) { + _eventQueueEntries.push_back(newEventQueueEntry(EventType_Complete, entry)); + } + + void EventQueue::event(TrackEntry* entry, Event* event) { + _eventQueueEntries.push_back(newEventQueueEntry(EventType_Event, entry, event)); + } + + /// Raises all events in the queue and drains the queue. + void EventQueue::drain() { + if (_drainDisabled) { + return; + } + + _drainDisabled = true; + + AnimationState& state = _state; + + // Don't cache _eventQueueEntries.size() so callbacks can queue their own events (eg, call setAnimation in AnimationState_Complete). + for (int i = 0; i < _eventQueueEntries.size(); ++i) { + EventQueueEntry* queueEntry = _eventQueueEntries[i]; + TrackEntry* trackEntry = queueEntry->_entry; + + switch (queueEntry->_type) { + case EventType_Start: + case EventType_Interrupt: + case EventType_Complete: + trackEntry->_onAnimationEventFunc(&state, queueEntry->_type, trackEntry, NULL); + state._onAnimationEventFunc(&state, queueEntry->_type, trackEntry, NULL); + break; + case EventType_End: + trackEntry->_onAnimationEventFunc(&state, queueEntry->_type, trackEntry, NULL); + state._onAnimationEventFunc(&state, queueEntry->_type, trackEntry, NULL); + /* Yes, we want to fall through here */ + case EventType_Dispose: + trackEntry->_onAnimationEventFunc(&state, EventType_Dispose, trackEntry, NULL); + state._onAnimationEventFunc(&state, EventType_Dispose, trackEntry, NULL); + trackEntry->reset(); + _trackEntryPool.free(trackEntry); + break; + case EventType_Event: + trackEntry->_onAnimationEventFunc(&state, queueEntry->_type, trackEntry, queueEntry->_event); + state._onAnimationEventFunc(&state, queueEntry->_type, trackEntry, queueEntry->_event); + break; + } + } + _eventQueueEntries.clear(); + + _drainDisabled = false; + } + + const int AnimationState::Subsequent = 0; + const int AnimationState::First = 1; + const int AnimationState::Dip = 2; + const int AnimationState::DipMix = 3; + + AnimationState::AnimationState(AnimationStateData& data) : + _data(data), + _queue(EventQueue::newEventQueue(*this, _trackEntryPool)), + _animationsChanged(false), + _onAnimationEventFunc(dummyOnAnimationEventFunc), + _timeScale(1) { + // Empty + } + + AnimationState::~AnimationState() { + DESTROY(EventQueue, _queue); + } + + void AnimationState::update(float delta) { + delta *= _timeScale; + for (int i = 0, n = static_cast(_tracks.size()); i < n; ++i) { + TrackEntry* currentP = _tracks[i]; + if (currentP == NULL) { + continue; + } + + TrackEntry& current = *currentP; + + current._animationLast = current._nextAnimationLast; + current._trackLast = current._nextTrackLast; + + float currentDelta = delta * current._timeScale; + + if (current._delay > 0) { + current._delay -= currentDelta; + if (current._delay > 0) { + continue; + } + currentDelta = -current._delay; + current._delay = 0; + } + + TrackEntry* next = current._next; + if (next != NULL) { + // When the next entry's delay is passed, change to the next entry, preserving leftover time. + float nextTime = current._trackLast - next->_delay; + if (nextTime >= 0) { + next->_delay = 0; + next->_trackTime = nextTime + (delta * next->_timeScale); + current._trackTime += currentDelta; + setCurrent(i, next, true); + while (next->_mixingFrom != NULL) { + next->_mixTime += currentDelta; + next = next->_mixingFrom; + } + continue; + } + } + else if (current._trackLast >= current._trackEnd && current._mixingFrom == NULL) { + // clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom. + _tracks[i] = NULL; + + _queue->end(currentP); + disposeNext(currentP); + continue; + } + + if (current._mixingFrom != NULL && updateMixingFrom(currentP, delta)) { + // End mixing from entries once all have completed. + TrackEntry* from = current._mixingFrom; + current._mixingFrom = NULL; + while (from != NULL) { + _queue->end(from); + from = from->_mixingFrom; + } + } + + current._trackTime += currentDelta; + } + + _queue->drain(); + } + + bool AnimationState::apply(Skeleton& skeleton) { + if (_animationsChanged) { + animationsChanged(); + } + + bool applied = false; + for (int i = 0, n = static_cast(_tracks.size()); i < n; ++i) { + TrackEntry* currentP = _tracks[i]; + if (currentP == NULL || currentP->_delay > 0) { + continue; + } + + TrackEntry& current = *currentP; + + applied = true; + MixPose currentPose = i == 0 ? MixPose_Current : MixPose_CurrentLayered; + + // apply mixing from entries first. + float mix = current._alpha; + if (current._mixingFrom != NULL) { + mix *= applyMixingFrom(currentP, skeleton, currentPose); + } + else if (current._trackTime >= current._trackEnd && current._next == NULL) { + mix = 0; // Set to setup pose the last time the entry will be applied. + } + + // apply current entry. + float animationLast = current._animationLast, animationTime = current.getAnimationTime(); + int timelineCount = static_cast(current._animation->_timelines.size()); + Vector& timelines = current._animation->_timelines; + if (mix == 1) { + for (int ii = 0; ii < timelineCount; ++ii) { + timelines[ii]->apply(skeleton, animationLast, animationTime, &_events, 1, MixPose_Setup, MixDirection_In); + } + } + else { + Vector& timelineData = current._timelineData; + + bool firstFrame = current._timelinesRotation.size() == 0; + if (firstFrame) { + current._timelinesRotation.reserve(timelines.size() << 1); + current._timelinesRotation.setSize(timelines.size() << 1); + } + Vector& timelinesRotation = current._timelinesRotation; + + for (int ii = 0; ii < timelineCount; ++ii) { + Timeline* timeline = timelines[ii]; + assert(timeline); + + MixPose pose = timelineData[ii] >= AnimationState::First ? MixPose_Setup : currentPose; + + RotateTimeline* rotateTimeline = NULL; + if (timeline->getRTTI().derivesFrom(RotateTimeline::rtti)) { + rotateTimeline = static_cast(timeline); + } + + if (rotateTimeline != NULL) { + applyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, pose, timelinesRotation, ii << 1, firstFrame); + } + else { + timeline->apply(skeleton, animationLast, animationTime, &_events, mix, pose, MixDirection_In); + } + } + } + + queueEvents(currentP, animationTime); + _events.clear(); + current._nextAnimationLast = animationTime; + current._nextTrackLast = current._trackTime; + } + + _queue->drain(); + return applied; + } + + void AnimationState::clearTracks() { + bool oldDrainDisabled = _queue->_drainDisabled; + _queue->_drainDisabled = true; + for (int i = 0, n = static_cast(_tracks.size()); i < n; ++i) { + clearTrack(i); + } + _tracks.clear(); + _queue->_drainDisabled = oldDrainDisabled; + _queue->drain(); + } + + void AnimationState::clearTrack(int trackIndex) { + if (trackIndex >= _tracks.size()) { + return; + } + + TrackEntry* current = _tracks[trackIndex]; + if (current == NULL) { + return; + } + + _queue->end(current); + + disposeNext(current); + + TrackEntry* entry = current; + while (true) { + TrackEntry* from = entry->_mixingFrom; + if (from == NULL) { + break; + } + + _queue->end(from); + entry->_mixingFrom = NULL; + entry = from; + } + + _tracks[current->_trackIndex] = NULL; + + _queue->drain(); + } + + TrackEntry* AnimationState::setAnimation(int trackIndex, std::string animationName, bool loop) { + Animation* animation = _data._skeletonData.findAnimation(animationName); + assert(animation != NULL); + + return setAnimation(trackIndex, animation, loop); + } + + TrackEntry* AnimationState::setAnimation(int trackIndex, Animation* animation, bool loop) { + assert(animation != NULL); + + bool interrupt = true; + TrackEntry* current = expandToIndex(trackIndex); + if (current != NULL) { + if (current->_nextTrackLast == -1) { + // Don't mix from an entry that was never applied. + _tracks[trackIndex] = current->_mixingFrom; + _queue->interrupt(current); + _queue->end(current); + disposeNext(current); + current = current->_mixingFrom; + interrupt = false; + } + else { + disposeNext(current); + } + } + + TrackEntry* entry = newTrackEntry(trackIndex, animation, loop, current); + setCurrent(trackIndex, entry, interrupt); + _queue->drain(); + + return entry; + } + + TrackEntry* AnimationState::addAnimation(int trackIndex, std::string animationName, bool loop, float delay) { + Animation* animation = _data._skeletonData.findAnimation(animationName); + assert(animation != NULL); + + return addAnimation(trackIndex, animation, loop, delay); + } + + TrackEntry* AnimationState::addAnimation(int trackIndex, Animation* animation, bool loop, float delay) { + assert(animation != NULL); + + TrackEntry* last = expandToIndex(trackIndex); + if (last != NULL) { + while (last->_next != NULL) { + last = last->_next; + } + } + + TrackEntry* entry = newTrackEntry(trackIndex, animation, loop, last); + + if (last == NULL) { + setCurrent(trackIndex, entry, true); + _queue->drain(); + } + else { + last->_next = entry; + if (delay <= 0) { + float duration = last->_animationEnd - last->_animationStart; + if (duration != 0) { + if (last->_loop) { + delay += duration * (1 + (int)(last->_trackTime / duration)); + } else { + delay += duration; + } + delay -= _data.getMix(last->_animation, animation); + } else { + delay = 0; + } + } + } + + entry->_delay = delay; + return entry; + } + + TrackEntry* AnimationState::setEmptyAnimation(int trackIndex, float mixDuration) { + TrackEntry* entry = setAnimation(trackIndex, AnimationState::getEmptyAnimation(), false); + entry->_mixDuration = mixDuration; + entry->_trackEnd = mixDuration; + return entry; + } + + TrackEntry* AnimationState::addEmptyAnimation(int trackIndex, float mixDuration, float delay) { + if (delay <= 0) { + delay -= mixDuration; + } + + TrackEntry* entry = addAnimation(trackIndex, AnimationState::getEmptyAnimation(), false, delay); + entry->_mixDuration = mixDuration; + entry->_trackEnd = mixDuration; + return entry; + } + + void AnimationState::setEmptyAnimations(float mixDuration) { + bool oldDrainDisabled = _queue->_drainDisabled; + _queue->_drainDisabled = true; + for (int i = 0, n = static_cast(_tracks.size()); i < n; ++i) { + TrackEntry* current = _tracks[i]; + if (current != NULL) { + setEmptyAnimation(i, mixDuration); + } + } + _queue->_drainDisabled = oldDrainDisabled; + _queue->drain(); + } + + TrackEntry* AnimationState::getCurrent(int trackIndex) { + return trackIndex >= _tracks.size() ? NULL : _tracks[trackIndex]; + } + + AnimationStateData& AnimationState::getData() { + return _data; + } + + Vector AnimationState::getTracks() { + return _tracks; + } + + float AnimationState::getTimeScale() { + return _timeScale; + } + + void AnimationState::setTimeScale(float inValue) { + _timeScale = inValue; + } + + void AnimationState::setOnAnimationEventFunc(OnAnimationEventFunc inValue) { + _onAnimationEventFunc = inValue; + } + + void AnimationState::setRendererObject(void* inValue) { + _rendererObject = inValue; + } + + void* AnimationState::getRendererObject() { + return _rendererObject; + } + + Animation* AnimationState::getEmptyAnimation() { + static Vector timelines; + static Animation ret(std::string(""), timelines, 0); + return &ret; + } + + void AnimationState::applyRotateTimeline(RotateTimeline* rotateTimeline, Skeleton& skeleton, float time, float alpha, MixPose pose, Vector& timelinesRotation, int i, bool firstFrame) { + if (firstFrame) { + timelinesRotation[i] = 0; + } + + if (alpha == 1) { + rotateTimeline->apply(skeleton, 0, time, NULL, 1, pose, MixDirection_In); + return; + } + + Bone* bone = skeleton._bones[rotateTimeline->_boneIndex]; + Vector frames = rotateTimeline->_frames; + if (time < frames[0]) { + if (pose == MixPose_Setup) { + bone->_rotation = bone->_data._rotation; + } + return; + } + + float r2; + if (time >= frames[frames.size() - RotateTimeline::ENTRIES]) { + // Time is after last frame. + r2 = bone->_data._rotation + frames[frames.size() + RotateTimeline::PREV_ROTATION]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(frames, time, RotateTimeline::ENTRIES); + float prevRotation = frames[frame + RotateTimeline::PREV_ROTATION]; + float frameTime = frames[frame]; + float percent = rotateTimeline->getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + RotateTimeline::PREV_TIME] - frameTime)); + + r2 = frames[frame + RotateTimeline::ROTATION] - prevRotation; + r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360; + r2 = prevRotation + r2 * percent + bone->_data._rotation; + r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360; + } + + // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. + float r1 = pose == MixPose_Setup ? bone->_data._rotation : bone->_rotation; + float total, diff = r2 - r1; + if (diff == 0) { + total = timelinesRotation[i]; + } + else { + diff -= (16384 - (int)(16384.499999999996 - diff / 360)) * 360; + float lastTotal, lastDiff; + if (firstFrame) { + lastTotal = 0; + lastDiff = diff; + } + else { + lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. + lastDiff = timelinesRotation[i + 1]; // Difference between bones. + } + + bool current = diff > 0, dir = lastTotal >= 0; + // Detect cross at 0 (not 180). + if (sign(lastDiff) != sign(diff) && fabs(lastDiff) <= 90) { + // A cross after a 360 rotation is a loop. + if (fabs(lastTotal) > 180) { + lastTotal += 360 * sign(lastTotal); + } + dir = current; + } + + total = diff + lastTotal - fmod(lastTotal, 360); // Store loops as part of lastTotal. + if (dir != current) { + total += 360 * sign(lastTotal); + } + timelinesRotation[i] = total; + } + timelinesRotation[i + 1] = diff; + r1 += total * alpha; + bone->_rotation = r1 - (16384 - (int)(16384.499999999996 - r1 / 360)) * 360; + } + + bool AnimationState::updateMixingFrom(TrackEntry* to, float delta) { + TrackEntry* from = to->_mixingFrom; + if (from == NULL) { + return true; + } + + bool finished = updateMixingFrom(from, delta); + + // Require mixTime > 0 to ensure the mixing from entry was applied at least once. + if (to->_mixTime > 0 && (to->_mixTime >= to->_mixDuration || to->_timeScale == 0)) { + // Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame). + if (from->_totalAlpha == 0 || to->_mixDuration == 0) { + to->_mixingFrom = from->_mixingFrom; + to->_interruptAlpha = from->_interruptAlpha; + _queue->end(from); + } + return finished; + } + + from->_animationLast = from->_nextAnimationLast; + from->_trackLast = from->_nextTrackLast; + from->_trackTime += delta * from->_timeScale; + to->_mixTime += delta * to->_timeScale; + + return false; + } + + float AnimationState::applyMixingFrom(TrackEntry* to, Skeleton& skeleton, MixPose currentPose) { + TrackEntry* from = to->_mixingFrom; + if (from->_mixingFrom != NULL) { + applyMixingFrom(from, skeleton, currentPose); + } + + float mix; + if (to->_mixDuration == 0) { + // Single frame mix to undo mixingFrom changes. + mix = 1; + currentPose = MixPose_Setup; + } + else { + mix = to->_mixTime / to->_mixDuration; + if (mix > 1) { + mix = 1; + } + } + + Vector* eventBuffer = mix < from->_eventThreshold ? &_events : NULL; + bool attachments = mix < from->_attachmentThreshold, drawOrder = mix < from->_drawOrderThreshold; + float animationLast = from->_animationLast, animationTime = from->getAnimationTime(); + Vector& timelines = from->_animation->_timelines; + int timelineCount = static_cast(timelines.size()); + Vector& timelineData = from->_timelineData; + Vector& timelineDipMix = from->_timelineDipMix; + + bool firstFrame = from->_timelinesRotation.size() == 0; + if (firstFrame) { + // from.timelinesRotation.setSize + from->_timelinesRotation.reserve(timelines.size() << 1); + from->_timelinesRotation.setSize(timelines.size() << 1); + } + + Vector& timelinesRotation = from->_timelinesRotation; + + MixPose pose; + float alphaDip = from->_alpha * to->_interruptAlpha, alphaMix = alphaDip * (1 - mix), alpha; + from->_totalAlpha = 0; + for (int i = 0; i < timelineCount; ++i) { + Timeline* timeline = timelines[i]; + switch (timelineData[i]) { + case Subsequent: + if (!attachments && timeline->getRTTI().derivesFrom(AttachmentTimeline::rtti)) { + continue; + } + if (!drawOrder && timeline->getRTTI().derivesFrom(DrawOrderTimeline::rtti)) { + continue; + } + + pose = currentPose; + alpha = alphaMix; + break; + case First: + pose = MixPose_Setup; + alpha = alphaMix; + break; + case Dip: + pose = MixPose_Setup; + alpha = alphaDip; + break; + default: + pose = MixPose_Setup; + TrackEntry* dipMix = timelineDipMix[i]; + alpha = alphaDip * MAX(0, 1 - dipMix->_mixTime / dipMix->_mixDuration); + break; + } + from->_totalAlpha += alpha; + + RotateTimeline* rotateTimeline = NULL; + if (timeline->getRTTI().derivesFrom(RotateTimeline::rtti)) { + rotateTimeline = static_cast(timeline); + } + + if (rotateTimeline != NULL) { + applyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, pose, timelinesRotation, i << 1, firstFrame); + } + else { + timeline->apply(skeleton, animationLast, animationTime, eventBuffer, alpha, pose, MixDirection_Out); + } + } + + if (to->_mixDuration > 0) { + queueEvents(from, animationTime); + } + + _events.clear(); + from->_nextAnimationLast = animationTime; + from->_nextTrackLast = from->_trackTime; + + return mix; + } + + void AnimationState::queueEvents(TrackEntry* entry, float animationTime) { + float animationStart = entry->_animationStart, animationEnd = entry->_animationEnd; + float duration = animationEnd - animationStart; + float trackLastWrapped = fmodf(entry->_trackLast, duration); + + // Queue events before complete. + int i = 0, n = static_cast(_events.size()); + for (; i < n; ++i) { + Event* e = _events[i]; + if (e->_time < trackLastWrapped) { + break; + } + if (e->_time > animationEnd) { + // Discard events outside animation start/end. + continue; + } + _queue->event(entry, e); + } + + // Queue complete if completed a loop iteration or the animation. + if (entry->_loop ? (trackLastWrapped > fmod(entry->_trackTime, duration)) : (animationTime >= animationEnd && entry->_animationLast < animationEnd)) { + _queue->complete(entry); + } + + // Queue events after complete. + for (; i < n; ++i) { + Event* e = _events[i]; + if (e->_time < animationStart) { + // Discard events outside animation start/end. + continue; + } + _queue->event(entry, _events[i]); + } + } + + void AnimationState::setCurrent(int index, TrackEntry* current, bool interrupt) { + TrackEntry* from = expandToIndex(index); + _tracks[index] = current; + + if (from != NULL) { + if (interrupt) { + _queue->interrupt(from); + } + + current->_mixingFrom = from; + current->_mixTime = 0; + + // Store interrupted mix percentage. + if (from->_mixingFrom != NULL && from->_mixDuration > 0) { + current->_interruptAlpha *= MIN(1, from->_mixTime / from->_mixDuration); + } + + from->_timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in. + } + + _queue->start(current); // triggers animationsChanged + } + + TrackEntry* AnimationState::expandToIndex(int index) { + if (index < _tracks.size()) { + return _tracks[index]; + } + + while (index >= _tracks.size()) { + _tracks.push_back(NULL); + } + + return NULL; + } + + TrackEntry* AnimationState::newTrackEntry(int trackIndex, Animation* animation, bool loop, TrackEntry* last) { + TrackEntry* entryP = _trackEntryPool.obtain(); // Pooling + TrackEntry& entry = *entryP; + + entry._trackIndex = trackIndex; + entry._animation = animation; + entry._loop = loop; + + entry._eventThreshold = 0; + entry._attachmentThreshold = 0; + entry._drawOrderThreshold = 0; + + entry._animationStart = 0; + entry._animationEnd = animation->getDuration(); + entry._animationLast = -1; + entry._nextAnimationLast = -1; + + entry._delay = 0; + entry._trackTime = 0; + entry._trackLast = -1; + entry._nextTrackLast = -1; // nextTrackLast == -1 signifies a TrackEntry that wasn't applied yet. + entry._trackEnd = std::numeric_limits::max(); // loop ? float.MaxValue : animation.Duration; + entry._timeScale = 1; + + entry._alpha = 1; + entry._interruptAlpha = 1; + entry._mixTime = 0; + entry._mixDuration = (last == NULL) ? 0 : _data.getMix(last->_animation, animation); + + return entryP; + } + + void AnimationState::disposeNext(TrackEntry* entry) { + TrackEntry* next = entry->_next; + while (next != NULL) { + _queue->dispose(next); + next = next->_next; + } + entry->_next = NULL; + } + + void AnimationState::animationsChanged() { + _animationsChanged = false; + + _propertyIDs.clear(); + + for (int i = 0, n = static_cast(_tracks.size()); i < n; ++i) { + TrackEntry* entry = _tracks[i]; + if (entry != NULL) { + entry->setTimelineData(NULL, _mixingTo, _propertyIDs); + } + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/AnimationStateData.cpp b/spine-cpp/spine-cpp/src/spine/AnimationStateData.cpp new file mode 100644 index 000000000..5edb8b934 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/AnimationStateData.cpp @@ -0,0 +1,108 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +namespace Spine { + AnimationStateData::AnimationStateData(SkeletonData& skeletonData) : _skeletonData(skeletonData), _defaultMix(0) { + // Empty + } + + void AnimationStateData::setMix(std::string fromName, std::string toName, float duration) { + Animation* from = _skeletonData.findAnimation(fromName); + Animation* to = _skeletonData.findAnimation(toName); + + setMix(from, to, duration); + } + + void AnimationStateData::setMix(Animation* from, Animation* to, float duration) { + assert(from != NULL); + assert(to != NULL); + + AnimationPair key(from, to); + HashMap::Iterator i = _animationToMixTime.find(key); + _animationToMixTime.erase(i); + _animationToMixTime.insert(key, duration); + } + + float AnimationStateData::getMix(Animation* from, Animation* to) { + assert(from != NULL); + assert(to != NULL); + + AnimationPair key(from, to); + + HashMap::Iterator i = _animationToMixTime.find(key); + + if (i != _animationToMixTime.end()) { + return i.second(); + } + + return _defaultMix; + } + + SkeletonData& AnimationStateData::getSkeletonData() { + return _skeletonData; + } + + float AnimationStateData::getDefaultMix() { + return _defaultMix; + } + + void AnimationStateData::setDefaultMix(float inValue) { + _defaultMix = inValue; + } + + AnimationStateData::AnimationPair::AnimationPair(Animation* a1, Animation* a2) : _a1(a1), _a2(a2) { + // Empty + } + + bool AnimationStateData::AnimationPair::operator==(const AnimationPair &other) const { + return _a1->_name == other._a1->_name && _a2->_name == other._a2->_name; + } + + std::size_t AnimationStateData::HashAnimationPair::operator()(const Spine::AnimationStateData::AnimationPair& val) const { + std::size_t h1 = 7; + size_t strlen = val._a1->_name.length(); + for (int i = 0; i < strlen; ++i) { + h1 = h1 * 31 + val._a1->_name.at(i); + } + + std::size_t h2 = 7; + strlen = val._a2->_name.length(); + for (int i = 0; i < strlen; ++i) { + h2 = h2 * 31 + val._a2->_name.at(i); + } + + return (((h1 << 5) + h1) ^ h2); + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Atlas.cpp b/spine-cpp/spine-cpp/src/spine/Atlas.cpp new file mode 100644 index 000000000..01ddc728d --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Atlas.cpp @@ -0,0 +1,360 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace Spine { + Atlas::Atlas(const char* path, TextureLoader& textureLoader) : _textureLoader(textureLoader) { + int dirLength; + char *dir; + int length; + const char* data; + + /* Get directory from atlas path. */ + const char* lastForwardSlash = strrchr(path, '/'); + const char* lastBackwardSlash = strrchr(path, '\\'); + const char* lastSlash = lastForwardSlash > lastBackwardSlash ? lastForwardSlash : lastBackwardSlash; + if (lastSlash == path) lastSlash++; /* Never drop starting slash. */ + dirLength = (int)(lastSlash ? lastSlash - path : 0); + dir = MALLOC(char, dirLength + 1); + memcpy(dir, path, dirLength); + dir[dirLength] = '\0'; + + data = SPINE_EXTENSION->spineReadFile(path, &length); + if (data) { + load(data, length, dir); + } + + FREE(data); + FREE(dir); + } + + Atlas::Atlas(const char* data, int length, const char* dir, TextureLoader& textureLoader) : _textureLoader(textureLoader) { + load(data, length, dir); + } + + Atlas::~Atlas() { + ContainerUtil::cleanUpVectorOfPointers(_pages); + ContainerUtil::cleanUpVectorOfPointers(_regions); + } + + void Atlas::flipV() { + for (size_t i = 0, n = _regions.size(); i < n; ++i) { + AtlasRegion* regionP = _regions[i]; + AtlasRegion& region = *regionP; + region.v = 1 - region.v; + region.v2 = 1 - region.v2; + } + } + + AtlasRegion* Atlas::findRegion(std::string name) { + for (size_t i = 0, n = _regions.size(); i < n; ++i) { + if (_regions[i]->name == name) { + return _regions[i]; + } + } + + return NULL; + } + + void Atlas::dispose() { + for (size_t i = 0, n = _pages.size(); i < n; ++i) { + _textureLoader.unload(_pages[i]->rendererObject); + } + } + + void Atlas::load(const char* begin, int length, const char* dir) { + static const char* formatNames[] = { "", "Alpha", "Intensity", "LuminanceAlpha", "RGB565", "RGBA4444", "RGB888", "RGBA8888" }; + static const char* textureFilterNames[] = { "", "Nearest", "Linear", "MipMap", "MipMapNearestNearest", "MipMapLinearNearest", + "MipMapNearestLinear", "MipMapLinearLinear" }; + + int count; + const char* end = begin + length; + int dirLength = (int)strlen(dir); + int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\'; + + AtlasPage *page = NULL; + Str str; + Str tuple[4]; + + while (readLine(&begin, end, &str)) { + if (str.end - str.begin == 0) { + page = 0; + } + else if (!page) { + char* name = mallocString(&str); + char* path = MALLOC(char, dirLength + needsSlash + strlen(name) + 1); + memcpy(path, dir, dirLength); + if (needsSlash) { + path[dirLength] = '/'; + } + strcpy(path + dirLength + needsSlash, name); + + page = NEW(AtlasPage); + new (page) AtlasPage(std::string(name)); + + FREE(name); + + int tupleVal = readTuple(&begin, end, tuple); + assert(tupleVal == 2); + + /* size is only optional for an atlas packed with an old TexturePacker. */ + page->width = toInt(tuple); + page->height = toInt(tuple + 1); + assert(readTuple(&begin, end, tuple)); + + page->format = (Format)indexOf(formatNames, 8, tuple); + + assert(readTuple(&begin, end, tuple)); + page->minFilter = (TextureFilter)indexOf(textureFilterNames, 8, tuple); + page->magFilter = (TextureFilter)indexOf(textureFilterNames, 8, tuple + 1); + + assert(readValue(&begin, end, &str)); + + page->uWrap = TextureWrap_ClampToEdge; + page->vWrap = TextureWrap_ClampToEdge; + if (!equals(&str, "none")) { + if (str.end - str.begin == 1) { + if (*str.begin == 'x') { + page->uWrap = TextureWrap_Repeat; + } + else if (*str.begin == 'y') { + page->vWrap = TextureWrap_Repeat; + } + } + else if (equals(&str, "xy")) { + page->uWrap = TextureWrap_Repeat; + page->vWrap = TextureWrap_Repeat; + } + } + + _textureLoader.load(*page, std::string(path)); + + FREE(path); + + _pages.push_back(page); + } + else { + AtlasRegion* region = NEW(AtlasRegion); + new (region) AtlasRegion(); + + region->page = page; + region->name = mallocString(&str); + + assert(readValue(&begin, end, &str)); + region->rotate = equals(&str, "true"); + + assert(readTuple(&begin, end, tuple) == 2); + region->x = toInt(tuple); + region->y = toInt(tuple + 1); + + assert(readTuple(&begin, end, tuple) == 2); + region->width = toInt(tuple); + region->height = toInt(tuple + 1); + + region->u = region->x / (float)page->width; + region->v = region->y / (float)page->height; + if (region->rotate) { + region->u2 = (region->x + region->height) / (float)page->width; + region->v2 = (region->y + region->width) / (float)page->height; + } + else { + region->u2 = (region->x + region->width) / (float)page->width; + region->v2 = (region->y + region->height) / (float)page->height; + } + + count = readTuple(&begin, end, tuple); + assert(count); + + if (count == 4) { + /* split is optional */ + region->splits.reserve(4); + region->splits.setSize(4); + region->splits[0] = toInt(tuple); + region->splits[1] = toInt(tuple + 1); + region->splits[2] = toInt(tuple + 2); + region->splits[3] = toInt(tuple + 3); + + count = readTuple(&begin, end, tuple); + assert(count); + + if (count == 4) { + /* pad is optional, but only present with splits */ + region->pads.reserve(4); + region->pads.setSize(4); + region->pads[0] = toInt(tuple); + region->pads[1] = toInt(tuple + 1); + region->pads[2] = toInt(tuple + 2); + region->pads[3] = toInt(tuple + 3); + + assert(readTuple(&begin, end, tuple)); + } + } + + region->originalWidth = toInt(tuple); + region->originalHeight = toInt(tuple + 1); + + readTuple(&begin, end, tuple); + region->offsetX = toInt(tuple); + region->offsetY = toInt(tuple + 1); + + assert(readValue(&begin, end, &str)); + + region->index = toInt(&str); + + _regions.push_back(region); + } + } + } + + void Atlas::trim(Str* str) { + while (isspace((unsigned char)*str->begin) && str->begin < str->end) { + (str->begin)++; + } + + if (str->begin == str->end) { + return; + } + + str->end--; + + while (isspace((unsigned char)*str->end) && str->end >= str->begin) { + str->end--; + } + + str->end++; + } + + int Atlas::readLine(const char** begin, const char* end, Str* str) { + if (*begin == end) { + return 0; + } + + str->begin = *begin; + + /* Find next delimiter. */ + while (*begin != end && **begin != '\n') { + (*begin)++; + } + + str->end = *begin; + trim(str); + + if (*begin != end) { + (*begin)++; + } + + return 1; + } + + int Atlas::beginPast(Str* str, char c) { + const char* begin = str->begin; + while (1) { + char lastSkippedChar = *begin; + if (begin == str->end) { + return 0; + } + begin++; + if (lastSkippedChar == c) { + break; + } + } + str->begin = begin; + return 1; + } + + int Atlas::readValue(const char** begin, const char* end, Str* str) { + readLine(begin, end, str); + if (!beginPast(str, ':')) { + return 0; + } + + trim(str); + return 1; + } + + int Atlas::readTuple(const char** begin, const char* end, Str tuple[]) { + int i; + Str str = { NULL, NULL }; + readLine(begin, end, &str); + if (!beginPast(&str, ':')) { + return 0; + } + + for (i = 0; i < 3; ++i) { + tuple[i].begin = str.begin; + if (!beginPast(&str, ',')) { + break; + } + + tuple[i].end = str.begin - 2; + trim(&tuple[i]); + } + + tuple[i].begin = str.begin; + tuple[i].end = str.end; + trim(&tuple[i]); + + return i + 1; + } + + char* Atlas::mallocString(Str* str) { + int length = (int)(str->end - str->begin); + char* string = MALLOC(char, length + 1); + memcpy(string, str->begin, length); + string[length] = '\0'; + return string; + } + + int Atlas::indexOf(const char** array, int count, Str* str) { + int length = (int)(str->end - str->begin); + int i; + for (i = count - 1; i >= 0; i--) { + if (strncmp(array[i], str->begin, length) == 0) { + return i; + } + } + return 0; + } + + int Atlas::equals(Str* str, const char* other) { + return strncmp(other, str->begin, str->end - str->begin) == 0; + } + + int Atlas::toInt(Str* str) { + return (int)strtol(str->begin, (char**)&str->end, 10); + } +} diff --git a/spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.cpp b/spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.cpp new file mode 100644 index 000000000..ada9bde71 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.cpp @@ -0,0 +1,138 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Spine { + RTTI_IMPL(AtlasAttachmentLoader, AttachmentLoader); + + AtlasAttachmentLoader::AtlasAttachmentLoader(Vector& inAtlasArray) : AttachmentLoader(), _atlasArray(inAtlasArray) { + // Empty + } + + RegionAttachment* AtlasAttachmentLoader::newRegionAttachment(Skin& skin, std::string name, std::string path) { + AtlasRegion* regionP = findRegion(path); + assert(regionP != NULL); + + AtlasRegion& region = *regionP; + + RegionAttachment* attachmentP = NEW(RegionAttachment); + new (attachmentP) RegionAttachment(name); + + RegionAttachment& attachment = *attachmentP; + attachment._rendererObject = regionP; + attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment._regionOffsetX = region.offsetX; + attachment._regionOffsetY = region.offsetY; + attachment._regionWidth = region.width; + attachment._regionHeight = region.height; + attachment._regionOriginalWidth = region.originalWidth; + attachment._regionOriginalHeight = region.originalHeight; + + return attachmentP; + } + + MeshAttachment* AtlasAttachmentLoader::newMeshAttachment(Skin& skin, std::string name, std::string path) { + AtlasRegion* regionP = findRegion(path); + assert(regionP != NULL); + + AtlasRegion& region = *regionP; + + MeshAttachment* attachmentP = NEW(MeshAttachment); + new (attachmentP) MeshAttachment(name); + + MeshAttachment& attachment = *attachmentP; + attachment._rendererObject = regionP; + attachment._regionU = region.u; + attachment._regionV = region.v; + attachment._regionU2 = region.u2; + attachment._regionV2 = region.v2; + attachment._regionRotate = region.rotate; + attachment._regionOffsetX = region.offsetX; + attachment._regionOffsetY = region.offsetY; + attachment._regionWidth = region.width; + attachment._regionHeight = region.height; + attachment._regionOriginalWidth = region.originalWidth; + attachment._regionOriginalHeight = region.originalHeight; + + return attachmentP; + } + + BoundingBoxAttachment* AtlasAttachmentLoader::newBoundingBoxAttachment(Skin& skin, std::string name) { + BoundingBoxAttachment* attachmentP = NEW(BoundingBoxAttachment); + new (attachmentP) BoundingBoxAttachment(name); + + return attachmentP; + } + + PathAttachment* AtlasAttachmentLoader::newPathAttachment(Skin& skin, std::string name) { + PathAttachment* attachmentP = NEW(PathAttachment); + new (attachmentP) PathAttachment(name); + + return attachmentP; + } + + PointAttachment* AtlasAttachmentLoader::newPointAttachment(Skin& skin, std::string name) { + PointAttachment* attachmentP = NEW(PointAttachment); + new (attachmentP) PointAttachment(name); + + return attachmentP; + } + + ClippingAttachment* AtlasAttachmentLoader::newClippingAttachment(Skin& skin, std::string name) { + ClippingAttachment* attachmentP = NEW(ClippingAttachment); + new (attachmentP) ClippingAttachment(name); + + return attachmentP; + } + + AtlasRegion* AtlasAttachmentLoader::findRegion(std::string name) { + AtlasRegion* ret; + + for (int i = 0; i < _atlasArray.size(); i++) { + ret = _atlasArray[i]->findRegion(name); + if (ret != NULL) { + return ret; + } + } + + return NULL; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Attachment.cpp b/spine-cpp/spine-cpp/src/spine/Attachment.cpp new file mode 100644 index 000000000..779be06a6 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Attachment.cpp @@ -0,0 +1,45 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + RTTI_IMPL_NOPARENT(Attachment); + + Attachment::Attachment(std::string name) : _name(name) { + assert(_name.length() > 0); + } + + const std::string& Attachment::getName() { + return _name; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/AttachmentLoader.cpp b/spine-cpp/spine-cpp/src/spine/AttachmentLoader.cpp new file mode 100644 index 000000000..5d166a05c --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/AttachmentLoader.cpp @@ -0,0 +1,51 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL_NOPARENT(AttachmentLoader); + + AttachmentLoader::AttachmentLoader() { + // Empty + } + + AttachmentLoader::~AttachmentLoader() { + // Empty + } +} diff --git a/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp b/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp new file mode 100644 index 000000000..7c417a7c8 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(AttachmentTimeline, Timeline); + + AttachmentTimeline::AttachmentTimeline(int frameCount) : Timeline(), _slotIndex(0) { + _frames.reserve(frameCount); + _attachmentNames.reserve(frameCount); + + _frames.setSize(frameCount); + + for (int i = 0; i < frameCount; ++i) { + _attachmentNames.push_back(std::string("")); + } + } + + void AttachmentTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + assert(_slotIndex < skeleton._slots.size()); + + std::string attachmentName; + Slot* slotP = skeleton._slots[_slotIndex]; + Slot& slot = *slotP; + if (direction == MixDirection_Out && pose == MixPose_Setup) { + attachmentName = slot._data._attachmentName; + slot._attachment = attachmentName.length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, attachmentName); + return; + } + + if (time < _frames[0]) { + // Time is before first frame. + if (pose == MixPose_Setup) { + attachmentName = slot._data._attachmentName; + slot._attachment = attachmentName.length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, attachmentName); + } + return; + } + + int frameIndex; + if (time >= _frames[_frames.size() - 1]) { + // Time is after last frame. + frameIndex = static_cast(_frames.size()) - 1; + } + else { + frameIndex = Animation::binarySearch(_frames, time, 1) - 1; + } + + attachmentName = _attachmentNames[frameIndex]; + slot._attachment = attachmentName.length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, attachmentName); + } + + int AttachmentTimeline::getPropertyId() { + return ((int)TimelineType_Attachment << 24) + _slotIndex; + } + + void AttachmentTimeline::setFrame(int frameIndex, float time, std::string attachmentName) { + _frames[frameIndex] = time; + _attachmentNames[frameIndex] = attachmentName; + } + + int AttachmentTimeline::getSlotIndex() { + return _slotIndex; + } + + void AttachmentTimeline::setSlotIndex(int inValue) { + _slotIndex = inValue; + } + + Vector& AttachmentTimeline::getFrames() { + return _frames; + } + + void AttachmentTimeline::setFrames(Vector& inValue) { + _frames = inValue; + } + + Vector AttachmentTimeline::getAttachmentNames() { + return _attachmentNames; + } + + void AttachmentTimeline::setAttachmentNames(Vector& inValue) { + _attachmentNames = inValue; + } + + int AttachmentTimeline::getFrameCount() { + return static_cast(_frames.size()); + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Bone.cpp b/spine-cpp/spine-cpp/src/spine/Bone.cpp new file mode 100644 index 000000000..184b1c0e1 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Bone.cpp @@ -0,0 +1,573 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include + +namespace Spine { + RTTI_IMPL(Bone, Updatable); + + bool Bone::yDown = false; + + void Bone::setYDown(bool inValue) { + yDown = inValue; + } + + bool Bone::isYDown() { + return yDown; + } + + Bone::Bone(BoneData& data, Skeleton& skeleton, Bone* parent) : Updatable(), + _data(data), + _skeleton(skeleton), + _parent(parent), + _x(0), + _y(0), + _rotation(0), + _scaleX(0), + _scaleY(0), + _shearX(0), + _shearY(0), + _ax(0), + _ay(0), + _arotation(0), + _ascaleX(0), + _ascaleY(0), + _ashearX(0), + _ashearY(0), + _appliedValid(false), + _a(0), + _b(0), + _worldX(0), + _c(0), + _d(0), + _worldY(0), + _sorted(false) { + setToSetupPose(); + } + + void Bone::update() { + updateWorldTransform(_x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY); + } + + void Bone::updateWorldTransform() { + updateWorldTransform(_x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY); + } + + void Bone::updateWorldTransform(float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) { + _ax = x; + _ay = y; + _arotation = rotation; + _ascaleX = scaleX; + _ascaleY = scaleY; + _ashearX = shearX; + _ashearY = shearY; + _appliedValid = true; + Skeleton& skeleton = _skeleton; + + Bone* parent = _parent; + if (!parent) { + // Root bone. + float rotationY = rotation + 90 + shearY; + float la = MathUtil::cosDeg(rotation + shearX) * scaleX; + float lb = MathUtil::cosDeg(rotationY) * scaleY; + float lc = MathUtil::sinDeg(rotation + shearX) * scaleX; + float ld = MathUtil::sinDeg(rotationY) * scaleY; + if (_skeleton.getFlipX()) { + x = -x; + la = -la; + lb = -lb; + } + + if (_skeleton.getFlipY() != Bone::isYDown()) { + y = -y; + lc = -lc; + ld = -ld; + } + + _a = la; + _b = lb; + _c = lc; + _d = ld; + _worldX = x + _skeleton.getX(); + _worldY = y + _skeleton.getY(); + + return; + } + + float pa = parent->_a; + float pb = parent->_b; + float pc = parent->_c; + float pd = parent->_d; + + _worldX = pa * x + pb * y + parent->_worldX; + _worldY = pc * x + pd * y + parent->_worldY; + + switch (_data.getTransformMode()) { + case TransformMode_Normal: { + float rotationY = rotation + 90 + shearY; + float la = MathUtil::cosDeg(rotation + shearX) * scaleX; + float lb = MathUtil::cosDeg(rotationY) * scaleY; + float lc = MathUtil::sinDeg(rotation + shearX) * scaleX; + float ld = MathUtil::sinDeg(rotationY) * scaleY; + _a = pa * la + pb * lc; + _b = pa * lb + pb * ld; + _c = pc * la + pd * lc; + _d = pc * lb + pd * ld; + + return; + } + case TransformMode_OnlyTranslation: { + float rotationY = rotation + 90 + shearY; + _a = MathUtil::cosDeg(rotation + shearX) * scaleX; + _b = MathUtil::cosDeg(rotationY) * scaleY; + _c = MathUtil::sinDeg(rotation + shearX) * scaleX; + _d = MathUtil::sinDeg(rotationY) * scaleY; + + break; + } + case TransformMode_NoRotationOrReflection: { + float s = pa * pa + pc * pc, prx; + if (s > 0.0001f) { + s = fabs(pa * pd - pb * pc) / s; + pb = pc * s; + pd = pa * s; + prx = MathUtil::atan2(pc, pa) * RadDeg; + } + else { + pa = 0; + pc = 0; + prx = 90 - MathUtil::atan2(pd, pb) * RadDeg; + } + float rx = rotation + shearX - prx; + float ry = rotation + shearY - prx + 90; + float la = MathUtil::cosDeg(rx) * scaleX; + float lb = MathUtil::cosDeg(ry) * scaleY; + float lc = MathUtil::sinDeg(rx) * scaleX; + float ld = MathUtil::sinDeg(ry) * scaleY; + + _a = pa * la - pb * lc; + _b = pa * lb - pb * ld; + _c = pc * la + pd * lc; + _d = pc * lb + pd * ld; + + break; + } + case TransformMode_NoScale: + case TransformMode_NoScaleOrReflection: { + float cos = MathUtil::cosDeg(rotation); + float sin = MathUtil::sinDeg(rotation); + float za = pa * cos + pb * sin; + float zc = pc * cos + pd * sin; + float s = sqrt(za * za + zc * zc); + if (s > 0.00001f) { + s = 1 / s; + } + + za *= s; + zc *= s; + s = sqrt(za * za + zc * zc); + float r = SPINE_PI / 2 + MathUtil::atan2(zc, za); + float zb = MathUtil::cos(r) * s; + float zd = MathUtil::sin(r) * s; + float la = MathUtil::cosDeg(shearX) * scaleX; + float lb = MathUtil::cosDeg(90 + shearY) * scaleY; + float lc = MathUtil::sinDeg(shearX) * scaleX; + float ld = MathUtil::sinDeg(90 + shearY) * scaleY; + + if (_data.getTransformMode() != TransformMode_NoScaleOrReflection ? pa * pd - pb * pc < 0 : _skeleton.getFlipX() != _skeleton.getFlipY()) { + zb = -zb; + zd = -zd; + } + + _a = za * la + zb * lc; + _b = za * lb + zb * ld; + _c = zc * la + zd * lc; + _d = zc * lb + zd * ld; + + return; + } + } + + if (_skeleton.getFlipX()) { + _a = -_a; + _b = -_b; + } + + if (skeleton.getFlipY() != Bone::isYDown()) { + _c = -_c; + _d = -_d; + } + } + + void Bone::setToSetupPose() { + BoneData& data = _data; + _x = data.getX(); + _y = data.getY(); + _rotation = data.getRotation(); + _scaleX = data.getScaleX(); + _scaleY = data.getScaleY(); + _shearX = data.getShearX(); + _shearY = data.getShearY(); + } + + void Bone::worldToLocal(float worldX, float worldY, float& outLocalX, float& outLocalY) { + float a = _a; + float b = _b; + float c = _c; + float d = _d; + + float invDet = 1 / (a * d - b * c); + float x = worldX - _worldX; + float y = worldY - _worldY; + + outLocalX = (x * d * invDet - y * b * invDet); + outLocalY = (y * a * invDet - x * c * invDet); + } + + void Bone::localToWorld(float localX, float localY, float& outWorldX, float& outWorldY) { + outWorldX = localX * _a + localY * _b + _worldX; + outWorldY = localX * _c + localY * _d + _worldY; + } + + float Bone::worldToLocalRotation(float worldRotation) { + float sin = MathUtil::sinDeg(worldRotation); + float cos = MathUtil::cosDeg(worldRotation); + + return MathUtil::atan2(_a * sin - _c * cos, _d * cos - _b * sin) * RadDeg; + } + + float Bone::localToWorldRotation(float localRotation) { + float sin = MathUtil::sinDeg(localRotation); + float cos = MathUtil::cosDeg(localRotation); + + return MathUtil::atan2(cos * _c + sin * _d, cos * _a + sin * _b) * RadDeg; + } + + void Bone::rotateWorld(float degrees) { + float a = _a; + float b = _b; + float c = _c; + float d = _d; + + float cos = MathUtil::cosDeg(degrees); + float sin = MathUtil::sinDeg(degrees); + + _a = cos * a - sin * c; + _b = cos * b - sin * d; + _c = sin * a + cos * c; + _d = sin * b + cos * d; + + _appliedValid = false; + } + + float Bone::getWorldToLocalRotationX() { + Bone* parent = _parent; + if (!parent) { + return _arotation; + } + + float pa = parent->_a; + float pb = parent->_b; + float pc = parent->_c; + float pd = parent->_d; + float a = _a; + float c = _c; + + return MathUtil::atan2(pa * c - pc * a, pd * a - pb * c) * RadDeg; + } + + float Bone::getWorldToLocalRotationY() { + Bone* parent = _parent; + if (!parent) { + return _arotation; + } + + float pa = parent->_a; + float pb = parent->_b; + float pc = parent->_c; + float pd = parent->_d; + float b = _b; + float d = _d; + + return MathUtil::atan2(pa * d - pc * b, pd * b - pb * d) * RadDeg; + } + + BoneData& Bone::getData() { + return _data; + } + + Skeleton& Bone::getSkeleton() { + return _skeleton; + } + + Bone* Bone::getParent() { + return _parent; + } + + Vector& Bone::getChildren() { + return _children; + } + + float Bone::getX() { + return _x; + } + + void Bone::setX(float inValue) { + _x = inValue; + } + + float Bone::getY() { + return _y; + } + + void Bone::setY(float inValue) { + _y = inValue; + } + + float Bone::getRotation() { + return _rotation; + } + + void Bone::setRotation(float inValue) { + _rotation = inValue; + } + + float Bone::getScaleX() { + return _scaleX; + } + + void Bone::setScaleX(float inValue) { + _scaleX = inValue; + } + + float Bone::getScaleY() { + return _scaleY; + } + + void Bone::setScaleY(float inValue) { + _scaleY = inValue; + } + + float Bone::getShearX() { + return _shearX; + } + + void Bone::setShearX(float inValue) { + _shearX = inValue; + } + + float Bone::getShearY() { + return _shearY; + } + + void Bone::setShearY(float inValue) { + _shearY = inValue; + } + + float Bone::getAppliedRotation() { + return _arotation; + } + + void Bone::setAppliedRotation(float inValue) { + _arotation = inValue; + } + + float Bone::getAX() { + return _ax; + } + + void Bone::setAX(float inValue) { + _ax = inValue; + } + + float Bone::getAY() { + return _ay; + } + + void Bone::setAY(float inValue) { + _ay = inValue; + } + + float Bone::getAScaleX() { + return _ascaleX; + } + + void Bone::setAScaleX(float inValue) { + _ascaleX = inValue; + } + + float Bone::getAScaleY() { + return _ascaleY; + } + + void Bone::setAScaleY(float inValue) { + _ascaleY = inValue; + } + + float Bone::getAShearX() { + return _ashearX; + } + + void Bone::setAShearX(float inValue) { + _ashearX = inValue; + } + + float Bone::getAShearY() { + return _ashearY; + } + + void Bone::setAShearY(float inValue) { + _ashearY = inValue; + } + + float Bone::getA() { + 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; + } + + float Bone::getWorldRotationY() { + return MathUtil::atan2(_d, _b) * RadDeg; + } + + float Bone::getWorldScaleX() { + return sqrt(_a * _a + _c * _c); + } + + float Bone::getWorldScaleY() { + return sqrt(_b * _b + _d * _d); + } + + void Bone::updateAppliedTransform() { + _appliedValid = true; + Bone* parent = _parent; + if (!parent) { + _ax = _worldX; + _ay = _worldY; + _arotation = MathUtil::atan2(_c, _a) * RadDeg; + _ascaleX = sqrt(_a * _a + _c * _c); + _ascaleY = sqrt(_b * _b + _d * _d); + _ashearX = 0; + _ashearY = MathUtil::atan2(_a * _b + _c * _d, _a * _d - _b * _c) * RadDeg; + + return; + } + + float pa = parent->_a; + float pb = parent->_b; + float pc = parent->_c; + float pd = parent->_d; + + float pid = 1 / (pa * pd - pb * pc); + float dx = _worldX - parent->_worldX; + float dy = _worldY - parent->_worldY; + + _ax = (dx * pd * pid - dy * pb * pid); + _ay = (dy * pa * pid - dx * pc * pid); + + float ia = pid * pd; + float id = pid * pa; + float ib = pid * pb; + float ic = pid * pc; + + float ra = ia * _a - ib * _c; + float rb = ia * _b - ib * _d; + float rc = id * _c - ic * _a; + float rd = id * _d - ic * _b; + + _ashearX = 0; + _ascaleX = sqrt(ra * ra + rc * rc); + + if (_ascaleX > 0.0001f) { + float det = ra * rd - rb * rc; + _ascaleY = det / _ascaleX; + _ashearY = MathUtil::atan2(ra * rb + rc * rd, det) * RadDeg; + _arotation = MathUtil::atan2(rc, ra) * RadDeg; + } + else { + _ascaleX = 0; + _ascaleY = sqrt(rb * rb + rd * rd); + _ashearY = 0; + _arotation = 90 - MathUtil::atan2(rd, rb) * RadDeg; + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/BoneData.cpp b/spine-cpp/spine-cpp/src/spine/BoneData.cpp new file mode 100644 index 000000000..f0cbb9502 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/BoneData.cpp @@ -0,0 +1,136 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + BoneData::BoneData(int index, std::string name, BoneData* parent) : + _index(index), + _name(name), + _parent(parent), + _length(0), + _x(0), + _y(0), + _rotation(0), + _scaleX(1), + _scaleY(1), + _shearX(0), + _shearY(0), + _transformMode(TransformMode_Normal) { + assert(index >= 0); + assert(_name.length() > 0); + } + + const int BoneData::getIndex() { + return _index; + } + + const std::string& BoneData::getName() { + return _name; + } + + BoneData* BoneData::getParent() { + return _parent; + } + + float BoneData::getLength() { + return _length; + } + + void BoneData::setLength(float inValue) { + _length = inValue; + } + + float BoneData::getX() { + return _x; + } + + void BoneData::setX(float inValue) { + _x = inValue; + } + + float BoneData::getY() { + return _y; + } + + void BoneData::setY(float inValue) { + _y = inValue; + } + + float BoneData::getRotation() { + return _rotation; + } + + void BoneData::setRotation(float inValue) { + _rotation = inValue; + } + + float BoneData::getScaleX() { + return _scaleX; + } + + void BoneData::setScaleX(float inValue) { + _scaleX = inValue; + } + + float BoneData::getScaleY() { + return _scaleY; + } + + void BoneData::setScaleY(float inValue) { + _scaleY = inValue; + } + + float BoneData::getShearX() { + return _shearX; + } + + void BoneData::setShearX(float inValue) { + _shearX = inValue; + } + + float BoneData::getShearY() { + return _shearY; + } + + void BoneData::setShearY(float inValue) { + _shearY = inValue; + } + + TransformMode BoneData::getTransformMode() { + return _transformMode; + } + + void BoneData::setTransformMode(TransformMode inValue) { + _transformMode = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/BoundingBoxAttachment.cpp b/spine-cpp/spine-cpp/src/spine/BoundingBoxAttachment.cpp new file mode 100644 index 000000000..41148ae80 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/BoundingBoxAttachment.cpp @@ -0,0 +1,39 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +namespace Spine { + RTTI_IMPL(BoundingBoxAttachment, VertexAttachment); + + BoundingBoxAttachment::BoundingBoxAttachment(std::string name) : VertexAttachment(name) { + // Empty + } +} diff --git a/spine-cpp/spine-cpp/src/spine/ClippingAttachment.cpp b/spine-cpp/spine-cpp/src/spine/ClippingAttachment.cpp new file mode 100644 index 000000000..644ba9c39 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/ClippingAttachment.cpp @@ -0,0 +1,49 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + RTTI_IMPL(ClippingAttachment, VertexAttachment); + + ClippingAttachment::ClippingAttachment(std::string name) : VertexAttachment(name), _endSlot(NULL) { + // Empty + } + + SlotData* ClippingAttachment::getEndSlot() { + return _endSlot; + } + + void ClippingAttachment::setEndSlot(SlotData* inValue) { + _endSlot = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp b/spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp new file mode 100644 index 000000000..7f2085829 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp @@ -0,0 +1,164 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(ColorTimeline, CurveTimeline); + + const int ColorTimeline::ENTRIES = 5; + const int ColorTimeline::PREV_TIME = -5; + const int ColorTimeline::PREV_R = -4; + const int ColorTimeline::PREV_G = -3; + const int ColorTimeline::PREV_B = -2; + const int ColorTimeline::PREV_A = -1; + const int ColorTimeline::R = 1; + const int ColorTimeline::G = 2; + const int ColorTimeline::B = 3; + const int ColorTimeline::A = 4; + + ColorTimeline::ColorTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0) { + _frames.reserve(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES); + } + + void ColorTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + Slot* slotP = skeleton._slots[_slotIndex]; + Slot& slot = *slotP; + if (time < _frames[0]) { + SlotData& slotData = slot._data; + switch (pose) { + case MixPose_Setup: + slot._r = slotData._r; + slot._g = slotData._g; + slot._b = slotData._b; + slot._a = slotData._a; + return; + case MixPose_Current: + slot._r += (slot._r - slotData._r) * alpha; + slot._g += (slot._g - slotData._g) * alpha; + slot._b += (slot._b - slotData._b) * alpha; + slot._a += (slot._a - slotData._a) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float r, g, b, a; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + int i = static_cast(_frames.size()); + r = _frames[i + PREV_R]; + g = _frames[i + PREV_G]; + b = _frames[i + PREV_B]; + a = _frames[i + PREV_A]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + r = _frames[frame + PREV_R]; + g = _frames[frame + PREV_G]; + b = _frames[frame + PREV_B]; + a = _frames[frame + PREV_A]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + r += (_frames[frame + R] - r) * percent; + g += (_frames[frame + G] - g) * percent; + b += (_frames[frame + B] - b) * percent; + a += (_frames[frame + A] - a) * percent; + } + + if (alpha == 1) { + slot._r = r; + slot._g = g; + slot._b = b; + slot._a = a; + } + else { + float br, bg, bb, ba; + if (pose == MixPose_Setup) { + br = slot._data._r; + bg = slot._data._g; + bb = slot._data._b; + ba = slot._data._a; + } + else { + br = slot._r; + bg = slot._g; + bb = slot._b; + ba = slot._a; + } + slot._r = br + ((r - br) * alpha); + slot._g = bg + ((g - bg) * alpha); + slot._b = bb + ((b - bb) * alpha); + slot._a = ba + ((a - ba) * alpha); + } + } + + int ColorTimeline::getPropertyId() { + return ((int)TimelineType_Color << 24) + _slotIndex; + } + + void ColorTimeline::setFrame(int frameIndex, float time, float r, float g, float b, float a) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + R] = r; + _frames[frameIndex + G] = g; + _frames[frameIndex + B] = b; + _frames[frameIndex + A] = a; + } + + int ColorTimeline::getSlotIndex() { + return _slotIndex; + } + + void ColorTimeline::setSlotIndex(int inValue) { + _slotIndex = inValue; + } + + Vector& ColorTimeline::getFrames() { + return _frames; + } + + void ColorTimeline::setFrames(Vector& inValue) { + _frames = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Constraint.cpp b/spine-cpp/spine-cpp/src/spine/Constraint.cpp new file mode 100644 index 000000000..4f1a797cd --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Constraint.cpp @@ -0,0 +1,43 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include + +namespace Spine { + RTTI_IMPL(Constraint, Updatable); + + Constraint::Constraint() { + // Empty + } + + Constraint::~Constraint() { + // Empty + } +} diff --git a/spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp b/spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp new file mode 100644 index 000000000..63571800d --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp @@ -0,0 +1,124 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + RTTI_IMPL(CurveTimeline, Timeline); + + const float CurveTimeline::LINEAR = 0; + const float CurveTimeline::STEPPED = 1; + const float CurveTimeline::BEZIER = 2; + const int CurveTimeline::BEZIER_SIZE = 10 * 2 - 1; + + CurveTimeline::CurveTimeline(int frameCount) { + assert(frameCount > 0); + + _curves.reserve((frameCount - 1) * BEZIER_SIZE); + _curves.setSize((frameCount - 1) * BEZIER_SIZE); + } + + int CurveTimeline::getFrameCount() { + return static_cast(_curves.size() / BEZIER_SIZE + 1); + } + + void CurveTimeline::setLinear(int frameIndex) { + _curves[frameIndex * BEZIER_SIZE] = LINEAR; + } + + void CurveTimeline::setStepped(int frameIndex) { + _curves[frameIndex * BEZIER_SIZE] = STEPPED; + } + + void CurveTimeline::setCurve(int frameIndex, float cx1, float cy1, float cx2, float cy2) { + float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f; + float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f; + float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy; + float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f; + + int i = frameIndex * BEZIER_SIZE; + _curves[i++] = BEZIER; + + float x = dfx, y = dfy; + for (int n = i + BEZIER_SIZE - 1; i < n; i += 2) { + _curves[i] = x; + _curves[i + 1] = y; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + } + + float CurveTimeline::getCurvePercent(int frameIndex, float percent) { + percent = clamp(percent, 0, 1); + int i = frameIndex * BEZIER_SIZE; + float type = _curves[i]; + + if (type == LINEAR) { + return percent; + } + + if (type == STEPPED) { + return 0; + } + + i++; + float x = 0; + for (int start = i, n = i + BEZIER_SIZE - 1; i < n; i += 2) { + x = _curves[i]; + if (x >= percent) { + float prevX, prevY; + if (i == start) { + prevX = 0; + prevY = 0; + } + else { + prevX = _curves[i - 2]; + prevY = _curves[i - 1]; + } + + return prevY + (_curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + } + } + + float y = _curves[i - 1]; + + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + } + + float CurveTimeline::getCurveType(int frameIndex) { + return _curves[frameIndex * BEZIER_SIZE]; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp b/spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp new file mode 100644 index 000000000..a9941702e --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp @@ -0,0 +1,235 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(DeformTimeline, CurveTimeline); + + DeformTimeline::DeformTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0), _attachment(NULL) { + _frames.reserve(frameCount); + _frameVertices.reserve(frameCount); + + _frames.setSize(frameCount); + + for (int i = 0; i < frameCount; ++i) { + Vector vec; + _frameVertices.push_back(vec); + } + } + + void DeformTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + Slot* slotP = skeleton._slots[_slotIndex]; + Slot& slot = *slotP; + + if (slot._attachment == NULL || !slot._attachment->getRTTI().derivesFrom(VertexAttachment::rtti)) { + return; + } + + VertexAttachment* vertexAttachment = static_cast(slot._attachment); + if (!vertexAttachment->applyDeform(_attachment)) { + return; + } + + Vector& vertices = slot._attachmentVertices; + if (vertices.size() == 0) { + alpha = 1; + } + + int vertexCount = static_cast(_frameVertices[0].size()); + + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + vertices.clear(); + return; + case MixPose_Current: + if (alpha == 1) { + vertices.clear(); + return; + } + + // Ensure size and preemptively set count. + vertices.reserve(vertexCount); + vertices.setSize(vertexCount); + + if (vertexAttachment->_bones.size() == 0) { + // Unweighted vertex positions. + Vector& setupVertices = vertexAttachment->_vertices; + for (int i = 0; i < vertexCount; ++i) { + vertices[i] += (setupVertices[i] - vertices[i]) * alpha; + } + } + else { + // Weighted deform offsets. + alpha = 1 - alpha; + for (int i = 0; i < vertexCount; ++i) { + vertices[i] *= alpha; + } + } + return; + default: + return; + } + } + + // Ensure size and preemptively set count. + vertices.reserve(vertexCount); + vertices.setSize(vertexCount); + + if (time >= _frames[_frames.size() - 1]) { + // Time is after last frame. + Vector& lastVertices = _frameVertices[_frames.size() - 1]; + if (alpha == 1) { + // Vertex positions or deform offsets, no alpha. + vertices.clear(); + for (int i = 0; i < vertexCount; ++i) { + float vertex = lastVertices[i]; + vertices.push_back(vertex); + } + } + else if (pose == MixPose_Setup) { + if (vertexAttachment->_bones.size() == 0) { + // Unweighted vertex positions, with alpha. + Vector& setupVertices = vertexAttachment->_vertices; + for (int i = 0; i < vertexCount; i++) { + float setup = setupVertices[i]; + vertices[i] = setup + (lastVertices[i] - setup) * alpha; + } + } + else { + // Weighted deform offsets, with alpha. + for (int i = 0; i < vertexCount; ++i) { + vertices[i] = lastVertices[i] * alpha; + } + } + } + else { + // Vertex positions or deform offsets, with alpha. + for (int i = 0; i < vertexCount; ++i) { + vertices[i] += (lastVertices[i] - vertices[i]) * alpha; + } + } + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time); + Vector& prevVertices = _frameVertices[frame - 1]; + Vector& nextVertices = _frameVertices[frame]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame - 1, 1 - (time - frameTime) / (_frames[frame - 1] - frameTime)); + + if (alpha == 1) { + // Vertex positions or deform offsets, no alpha. + for (int i = 0; i < vertexCount; ++i) { + float prev = prevVertices[i]; + vertices[i] = prev + (nextVertices[i] - prev) * percent; + } + } + else if (pose == MixPose_Setup) { + if (vertexAttachment->_bones.size() == 0) { + // Unweighted vertex positions, with alpha. + Vector& setupVertices = vertexAttachment->_vertices; + for (int i = 0; i < vertexCount; ++i) { + float prev = prevVertices[i], setup = setupVertices[i]; + vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; + } + } + else { + // Weighted deform offsets, with alpha. + for (int i = 0; i < vertexCount; ++i) { + float prev = prevVertices[i]; + vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; + } + } + } + else { + // Vertex positions or deform offsets, with alpha. + for (int i = 0; i < vertexCount; ++i) { + float prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; + } + } + } + + int DeformTimeline::getPropertyId() { + assert(_attachment != NULL); + + return ((int)TimelineType_Deform << 24) + _attachment->_id + _slotIndex; + } + + void DeformTimeline::setFrame(int frameIndex, float time, Vector& vertices) { + _frames[frameIndex] = time; + _frameVertices[frameIndex] = vertices; + } + + int DeformTimeline::getSlotIndex() { + return _slotIndex; + } + + void DeformTimeline::setSlotIndex(int inValue) { + _slotIndex = inValue; + } + + Vector& DeformTimeline::getFrames() { + return _frames; + } + + void DeformTimeline::setFrames(Vector& inValue) { + _frames = inValue; + } + + Vector< Vector >& DeformTimeline::getVertices() { + return _frameVertices; + } + + void DeformTimeline::setVertices(Vector< Vector >& inValue) { + _frameVertices = inValue; + } + + VertexAttachment* DeformTimeline::getAttachment() { + return _attachment; + } + + void DeformTimeline::setAttachment(VertexAttachment* inValue) { + _attachment = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp b/spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp new file mode 100644 index 000000000..079cbcbca --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp @@ -0,0 +1,130 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(DrawOrderTimeline, Timeline); + + DrawOrderTimeline::DrawOrderTimeline(int frameCount) : Timeline() { + _frames.reserve(frameCount); + _drawOrders.reserve(frameCount); + + _frames.setSize(frameCount); + + for (int i = 0; i < frameCount; ++i) { + Vector vec; + _drawOrders.push_back(vec); + } + } + + void DrawOrderTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + Vector& drawOrder = skeleton._drawOrder; + Vector& slots = skeleton._slots; + if (direction == MixDirection_Out && pose == MixPose_Setup) { + drawOrder.clear(); + drawOrder.reserve(slots.size()); + for (int i = 0, n = static_cast(slots.size()); i < n; ++i) { + drawOrder.push_back(slots[i]); + } + return; + } + + if (time < _frames[0]) { + if (pose == MixPose_Setup) { + drawOrder.clear(); + drawOrder.reserve(slots.size()); + for (int i = 0, n = static_cast(slots.size()); i < n; ++i) { + drawOrder.push_back(slots[i]); + } + } + return; + } + + int frame; + if (time >= _frames[_frames.size() - 1]) { + // Time is after last frame. + frame = static_cast(_frames.size()) - 1; + } + else { + frame = Animation::binarySearch(_frames, time) - 1; + } + + Vector& drawOrderToSetupIndex = _drawOrders[frame]; + if (drawOrderToSetupIndex.size() == 0) { + drawOrder.clear(); + for (int i = 0, n = static_cast(slots.size()); i < n; ++i) { + drawOrder.push_back(slots[i]); + } + } + else { + for (int i = 0, n = static_cast(drawOrderToSetupIndex.size()); i < n; ++i) { + drawOrder[i] = slots[drawOrderToSetupIndex[i]]; + } + } + } + + int DrawOrderTimeline::getPropertyId() { + return ((int)TimelineType_DrawOrder << 24); + } + + void DrawOrderTimeline::setFrame(int frameIndex, float time, Vector& drawOrder) { + _frames[frameIndex] = time; + _drawOrders[frameIndex] = drawOrder; + } + + Vector& DrawOrderTimeline::getFrames() { + return _frames; + } + + void DrawOrderTimeline::setFrames(Vector& inValue) { + _frames = inValue; + } + + Vector< Vector >& DrawOrderTimeline::getDrawOrders() { + return _drawOrders; + } + + void DrawOrderTimeline::setDrawOrders(Vector< Vector >& inValue) { + _drawOrders = inValue; + } + + int DrawOrderTimeline::getFrameCount() { + return static_cast(_frames.size()); + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Event.cpp b/spine-cpp/spine-cpp/src/spine/Event.cpp new file mode 100644 index 000000000..f5ef271d2 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Event.cpp @@ -0,0 +1,76 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + Event::Event(float time, const EventData& data) : + _data(data), + _time(time), + _intValue(0), + _floatValue(0), + _stringValue() { + // Empty + } + + const EventData& Event::getData() { + return _data; + } + + float Event::getTime() { + return _time; + } + + int Event::getIntValue() { + return _intValue; + } + + void Event::setIntValue(int inValue) { + _intValue = inValue; + } + + float Event::getFloatValue() { + return _floatValue; + } + + void Event::setFloatValue(int inValue) { + _floatValue = inValue; + } + + std::string Event::getStringValue() { + return _stringValue; + } + + void Event::setStringValue(std::string inValue) { + _stringValue = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/EventData.cpp b/spine-cpp/spine-cpp/src/spine/EventData.cpp new file mode 100644 index 000000000..1d390e459 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/EventData.cpp @@ -0,0 +1,72 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + EventData::EventData(std::string name) : + _name(name), + _intValue(0), + _floatValue(0), + _stringValue() { + assert(_name.length() > 0); + } + + /// The name of the event, which is unique within the skeleton. + const std::string& EventData::getName() { + return _name; + } + + int EventData::getIntValue() { + return _intValue; + } + + void EventData::setIntValue(int inValue) { + _intValue = inValue; + } + + float EventData::getFloatValue() { + return _floatValue; + } + + void EventData::setFloatValue(float inValue) { + _floatValue = inValue; + } + + std::string EventData::getStringValue() { + return _stringValue; + } + + void EventData::setStringValue(std::string inValue) { + _stringValue = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/EventTimeline.cpp b/spine-cpp/spine-cpp/src/spine/EventTimeline.cpp new file mode 100644 index 000000000..5fb00ccae --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/EventTimeline.cpp @@ -0,0 +1,120 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(EventTimeline, Timeline); + + EventTimeline::EventTimeline(int frameCount) : Timeline() { + _frames.reserve(frameCount); + _events.reserve(frameCount); + + _frames.setSize(frameCount); + _events.setSize(frameCount); + } + + EventTimeline::~EventTimeline() { + ContainerUtil::cleanUpVectorOfPointers(_events); + } + + void EventTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + if (pEvents == NULL) { + return; + } + + Vector& events = *pEvents; + + if (events.size() == 0) { + return; + } + + int frameCount = static_cast(_frames.size()); + + if (lastTime > time) { + // Fire events after last time for looped animations. + apply(skeleton, lastTime, std::numeric_limits::max(), pEvents, alpha, pose, direction); + lastTime = -1.0f; + } + else if (lastTime >= _frames[frameCount - 1]) { + // Last time is after last frame. + return; + } + + if (time < _frames[0]) { + return; // Time is before first frame. + } + + int frame; + if (lastTime < _frames[0]) { + frame = 0; + } + else { + frame = Animation::binarySearch(_frames, lastTime); + float frameTime = _frames[frame]; + while (frame > 0) { + // Fire multiple events with the same frame. + if (_frames[frame - 1] != frameTime) { + break; + } + frame--; + } + } + + for (; frame < frameCount && time >= _frames[frame]; ++frame) { + events.push_back(_events[frame]); + } + } + + int EventTimeline::getPropertyId() { + return ((int)TimelineType_Event << 24); + } + + void EventTimeline::setFrame(int frameIndex, Event* event) { + _frames[frameIndex] = event->getTime(); + _events[frameIndex] = event; + } + + Vector EventTimeline::getFrames() { return _frames; } + void EventTimeline::setFrames(Vector& inValue) { _frames = inValue; } // time, ... + Vector& EventTimeline::getEvents() { return _events; } + void EventTimeline::setEvents(Vector& inValue) { _events = inValue; } + int EventTimeline::getFrameCount() { return static_cast(_frames.size()); } +} diff --git a/spine-cpp/spine-cpp/src/spine/Extension.cpp b/spine-cpp/spine-cpp/src/spine/Extension.cpp new file mode 100644 index 000000000..4e1294cdb --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Extension.cpp @@ -0,0 +1,109 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include + +namespace Spine { + SpineExtension* SpineExtension::_instance = NULL; + + void SpineExtension::setInstance(SpineExtension* inValue) { + assert(!_instance); + + _instance = inValue; + } + + SpineExtension* SpineExtension::getInstance() { + assert(_instance); + + return _instance; + } + + SpineExtension::~SpineExtension() { + // Empty + } + + char* SpineExtension::spineReadFile(const char* path, int* length) { + char *data; + FILE *file = fopen(path, "rb"); + if (!file) return 0; + + fseek(file, 0, SEEK_END); + *length = (int)ftell(file); + fseek(file, 0, SEEK_SET); + + data = MALLOC(char, *length); + fread(data, 1, *length, file); + fclose(file); + + return data; + } + + SpineExtension::SpineExtension() { + // Empty + } + + DefaultSpineExtension* DefaultSpineExtension::getInstance() { + static DefaultSpineExtension ret; + return &ret; + } + + DefaultSpineExtension::~DefaultSpineExtension() { + // Empty + } + + void* DefaultSpineExtension::spineAlloc(size_t size, const char* file, int line) { + return malloc(size); + } + + void* DefaultSpineExtension::spineCalloc(size_t num, size_t size, const char* file, int line) { + void* ptr = spineAlloc(num * size, file, line); + if (ptr) { + memset(ptr, 0, num * size); + } + + return ptr; + } + + void* DefaultSpineExtension::spineRealloc(void* ptr, size_t size, const char* file, int line) { + return realloc(ptr, size); + } + + void DefaultSpineExtension::spineFree(void* mem) { + free(mem); + } + + DefaultSpineExtension::DefaultSpineExtension() : SpineExtension() { + // Empty + } +} diff --git a/spine-cpp/spine-cpp/src/spine/IkConstraint.cpp b/spine-cpp/spine-cpp/src/spine/IkConstraint.cpp new file mode 100644 index 000000000..13153d794 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/IkConstraint.cpp @@ -0,0 +1,314 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +namespace Spine { + RTTI_IMPL(IkConstraint, Constraint); + + void IkConstraint::apply(Bone& bone, float targetX, float targetY, float alpha) { + if (!bone._appliedValid) { + bone.updateAppliedTransform(); + } + + Bone* parent = bone.getParent(); + Bone& p = *parent; + + float id = 1 / (p._a * p._d - p._b * p._c); + float x = targetX - p._worldX, y = targetY - p._worldY; + float tx = (x * p._d - y * p._b) * id - bone._ax, ty = (y * p._a - x * p._c) * id - bone._ay; + float rotationIK = (float)atan2(ty, tx) * RadDeg - bone._ashearX - bone._arotation; + + if (bone._ascaleX < 0) { + rotationIK += 180; + } + + if (rotationIK > 180) { + rotationIK -= 360; + } + else if (rotationIK < -180) { + rotationIK += 360; + } + + bone.updateWorldTransform(bone._ax, bone._ay, bone._arotation + rotationIK * alpha, bone._ascaleX, bone._ascaleY, bone._ashearX, bone._ashearY); + } + + void IkConstraint::apply(Bone& parent, Bone& child, float targetX, float targetY, int bendDir, float alpha) { + if (areFloatsPracticallyEqual(alpha, 0)) { + child.updateWorldTransform(); + + return; + } + + if (!parent._appliedValid) { + parent.updateAppliedTransform(); + } + + if (!child._appliedValid) { + child.updateAppliedTransform(); + } + + float px = parent._ax; + float py = parent._ay; + float psx = parent._ascaleX; + float psy = parent._ascaleY; + float csx = child._ascaleX; + + int os1, os2, s2; + if (psx < 0) { + psx = -psx; + os1 = 180; + s2 = -1; + } + else { + os1 = 0; + s2 = 1; + } + + if (psy < 0) { + psy = -psy; + s2 = -s2; + } + + if (csx < 0) { + csx = -csx; + os2 = 180; + } + else { + os2 = 0; + } + + float cx = child._ax; + float cy; + float cwx; + float cwy; + float a = parent._a; + float b = parent._b; + float c = parent._c; + float d = parent._d; + + bool u = fabs(psx - psy) <= 0.0001f; + if (!u) { + cy = 0; + cwx = a * cx + parent._worldX; + cwy = c * cx + parent._worldY; + } + else { + cy = child._ay; + cwx = a * cx + b * cy + parent._worldX; + cwy = c * cx + d * cy + parent._worldY; + } + + Bone* parentparent = parent._parent; + Bone& pp = *parentparent; + + a = pp._a; + b = pp._b; + c = pp._c; + d = pp._d; + + float id = 1 / (a * d - b * c), x = targetX - pp._worldX, y = targetY - pp._worldY; + float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py; + x = cwx - pp._worldX; + y = cwy - pp._worldY; + float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; + float l1 = (float)sqrt(dx * dx + dy * dy), l2 = child._data.getLength() * csx, a1, a2; + if (u) { + l2 *= psx; + float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2); + if (cos < -1) { + cos = -1; + } + else if (cos > 1) { + cos = 1; + } + + a2 = (float)acos(cos) * bendDir; + a = l1 + l2 * cos; + b = l2 * (float)sin(a2); + a1 = (float)atan2(ty * a - tx * b, tx * a + ty * b); + } + else { + a = psx * l2; + b = psy * l2; + float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = (float)atan2(ty, tx); + c = bb * l1 * l1 + aa * dd - aa * bb; + float c1 = -2 * bb * l1, c2 = bb - aa; + d = c1 * c1 - 4 * c2 * c; + if (d >= 0) { + float q = (float)sqrt(d); + if (c1 < 0) q = -q; + q = -(c1 + q) / 2; + float r0 = q / c2, r1 = c / q; + float r = fabs(r0) < fabs(r1) ? r0 : r1; + if (r * r <= dd) { + y = (float)sqrt(dd - r * r) * bendDir; + a1 = ta - (float)atan2(y, r); + a2 = (float)atan2(y / psy, (r - l1) / psx); + + float os = (float)atan2(cy, cx) * s2; + float rotation = parent._arotation; + a1 = (a1 - os) * RadDeg + os1 - rotation; + if (a1 > 180) { + a1 -= 360; + } + else if (a1 < -180) { + a1 += 360; + } + + parent.updateWorldTransform(px, py, rotation + a1 * alpha, parent._scaleX, parent._ascaleY, 0, 0); + rotation = child._arotation; + a2 = ((a2 + os) * RadDeg - child._ashearX) * s2 + os2 - rotation; + + if (a2 > 180) { + a2 -= 360; + } + else if (a2 < -180) { + a2 += 360; + } + + child.updateWorldTransform(cx, cy, rotation + a2 * alpha, child._ascaleX, child._ascaleY, child._ashearX, child._ashearY); + + return; + } + } + + float minAngle = SPINE_PI, minX = l1 - a, minDist = minX * minX, minY = 0; + float maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; + c = -a * l1 / (aa - bb); + if (c >= -1 && c <= 1) { + c = (float)acos(c); + x = a * (float)cos(c) + l1; + y = b * (float)sin(c); + d = x * x + y * y; + + if (d < minDist) { + minAngle = c; + minDist = d; + minX = x; + minY = y; + } + + if (d > maxDist) { + maxAngle = c; + maxDist = d; + maxX = x; + maxY = y; + } + } + + if (dd <= (minDist + maxDist) / 2) { + a1 = ta - (float)atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } + else { + a1 = ta - (float)atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } + } + } + + IkConstraint::IkConstraint(IkConstraintData& data, Skeleton& skeleton) : Constraint(), + _data(data), + _mix(data.getMix()), + _bendDirection(data.getBendDirection()), + _target(skeleton.findBone(data.getTarget()->getName())) { + _bones.reserve(_data.getBones().size()); + for (BoneData** i = _data.getBones().begin(); i != _data.getBones().end(); ++i) { + BoneData* boneData = (*i); + + _bones.push_back(skeleton.findBone(boneData->getName())); + } + } + + /// Applies the constraint to the constrained bones. + void IkConstraint::apply() { + update(); + } + + void IkConstraint::update() { + switch (_bones.size()) { + case 1: { + Bone* bone0 = _bones[0]; + apply(*bone0, _target->getWorldX(), _target->getWorldY(), _mix); + } + break; + case 2: { + Bone* bone0 = _bones[0]; + Bone* bone1 = _bones[1]; + apply(*bone0, *bone1, _target->getWorldX(), _target->getWorldY(), _bendDirection, _mix); + } + break; + } + } + + int IkConstraint::getOrder() { + return _data.getOrder(); + } + + IkConstraintData& IkConstraint::getData() { + return _data; + } + + Vector& IkConstraint::getBones() { + return _bones; + } + + Bone* IkConstraint::getTarget() { + return _target; + } + + void IkConstraint::setTarget(Bone* inValue) { + _target = inValue; + } + + int IkConstraint::getBendDirection() { + return _bendDirection; + } + + void IkConstraint::setBendDirection(int inValue) { + _bendDirection = inValue; + } + + float IkConstraint::getMix() { + return _mix; + } + + void IkConstraint::setMix(float inValue) { + _mix = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/IkConstraintData.cpp b/spine-cpp/spine-cpp/src/spine/IkConstraintData.cpp new file mode 100644 index 000000000..416d5cebe --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/IkConstraintData.cpp @@ -0,0 +1,84 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + IkConstraintData::IkConstraintData(std::string name) : + _name(name), + _order(0), + _target(NULL), + _bendDirection(1), + _mix(1) { + // Empty + } + + const std::string& IkConstraintData::getName() { + return _name; + } + + int IkConstraintData::getOrder() { + return _order; + } + + void IkConstraintData::setOrder(int inValue) { + _order = inValue; + } + + Vector& IkConstraintData::getBones() { + return _bones; + } + + BoneData* IkConstraintData::getTarget() { + return _target; + } + + void IkConstraintData::setTarget(BoneData* inValue) { + _target = inValue; + } + + int IkConstraintData::getBendDirection() { + return _bendDirection; + } + + void IkConstraintData::setBendDirection(int inValue) { + _bendDirection = inValue; + } + + float IkConstraintData::getMix() { + return _mix; + } + + void IkConstraintData::setMix(float inValue) { + _mix = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp b/spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp new file mode 100644 index 000000000..6bcab4b74 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp @@ -0,0 +1,121 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(IkConstraintTimeline, CurveTimeline); + + const int IkConstraintTimeline::ENTRIES = 3; + const int IkConstraintTimeline::PREV_TIME = -3; + const int IkConstraintTimeline::PREV_MIX = -2; + const int IkConstraintTimeline::PREV_BEND_DIRECTION = -1; + const int IkConstraintTimeline::MIX = 1; + const int IkConstraintTimeline::BEND_DIRECTION = 2; + + IkConstraintTimeline::IkConstraintTimeline(int frameCount) : CurveTimeline(frameCount), _ikConstraintIndex(0) { + _frames.reserve(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES); + } + + void IkConstraintTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + IkConstraint* constraintP = skeleton._ikConstraints[_ikConstraintIndex]; + IkConstraint& constraint = *constraintP; + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + constraint._mix = constraint._data._mix; + constraint._bendDirection = constraint._data._bendDirection; + return; + case MixPose_Current: + constraint._mix += (constraint._data._mix - constraint._mix) * alpha; + constraint._bendDirection = constraint._data._bendDirection; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + if (pose == MixPose_Setup) { + constraint._mix = constraint._data._mix + (_frames[_frames.size() + PREV_MIX] - constraint._data._mix) * alpha; + constraint._bendDirection = direction == MixDirection_Out ? constraint._data._bendDirection + : (int)_frames[_frames.size() + PREV_BEND_DIRECTION]; + } + else { + constraint._mix += (_frames[_frames.size() + PREV_MIX] - constraint._mix) * alpha; + if (direction == MixDirection_In) { + constraint._bendDirection = (int)_frames[_frames.size() + PREV_BEND_DIRECTION]; + } + } + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + float mix = _frames[frame + PREV_MIX]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + if (pose == MixPose_Setup) { + constraint._mix = constraint._data._mix + (mix + (_frames[frame + MIX] - mix) * percent - constraint._data._mix) * alpha; + constraint._bendDirection = direction == MixDirection_Out ? constraint._data._bendDirection : (int)_frames[frame + PREV_BEND_DIRECTION]; + } + else { + constraint._mix += (mix + (_frames[frame + MIX] - mix) * percent - constraint._mix) * alpha; + if (direction == MixDirection_In) { + constraint._bendDirection = (int)_frames[frame + PREV_BEND_DIRECTION]; + } + } + } + + int IkConstraintTimeline::getPropertyId() { + return ((int)TimelineType_IkConstraint << 24) + _ikConstraintIndex; + } + + void IkConstraintTimeline::setFrame(int frameIndex, float time, float mix, int bendDirection) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + MIX] = mix; + _frames[frameIndex + BEND_DIRECTION] = bendDirection; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Json.cpp b/spine-cpp/spine-cpp/src/spine/Json.cpp new file mode 100644 index 000000000..2a5494f00 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Json.cpp @@ -0,0 +1,560 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +/* Json */ +/* JSON parser in CPP, shamelessly ripped from json.c in the spine-c runtime */ + +#ifndef _DEFAULT_SOURCE +/* Bring strings.h definitions into string.h, where appropriate */ +#define _DEFAULT_SOURCE +#endif + +#ifndef _BSD_SOURCE +/* Bring strings.h definitions into string.h, where appropriate */ +#define _BSD_SOURCE +#endif + +#include + +#include +#include +#include +#include +#include /* strtod (C89), strtof (C99) */ +#include /* strcasecmp (4.4BSD - compatibility), _stricmp (_WIN32) */ +#include +#include + +namespace Spine { + const int Json::JSON_FALSE = 0; + const int Json::JSON_TRUE = 1; + const int Json::JSON_NULL = 2; + const int Json::JSON_NUMBER = 3; + const int Json::JSON_STRING = 4; + const int Json::JSON_ARRAY = 5; + const int Json::JSON_OBJECT = 6; + + const char* Json::_error = NULL; + + Json* Json::getItem(Json *object, const char* string) { + Json *c = object->_child; + while (c && json_strcasecmp(c->_name, string)) { + c = c->_next; + } + return c; + } + + const char* Json::getString(Json *object, const char* name, const char* defaultValue) { + object = getItem(object, name); + if (object) { + return object->_valueString; + } + + return defaultValue; + } + + float Json::getFloat(Json *value, const char* name, float defaultValue) { + value = getItem(value, name); + return value ? value->_valueFloat : defaultValue; + } + + int Json::getInt(Json *value, const char* name, int defaultValue) { + value = getItem(value, name); + return value ? value->_valueInt : defaultValue; + } + + const char* Json::getError() { + return _error; + } + + Json::Json(const char* value) : + _next(NULL), +#if SPINE_JSON_HAVE_PREV + _prev(NULL), +#endif + _child(NULL), + _type(0), + _size(0), + _valueString(NULL), + _valueInt(0), + _valueFloat(0), + _name(NULL) { + if (value) { + value = parseValue(this, skip(value)); + + assert(value); + } + } + + Json::~Json() { + if (_child) { + DESTROY(Json, _child); + } + + if (_valueString) { + FREE(_valueString); + } + + if (_name) { + FREE(_name); + } + + if (_next) { + DESTROY(Json, _next); + } + } + + const char* Json::skip(const char* inValue) { + if (!inValue) { + /* must propagate NULL since it's often called in skip(f(...)) form */ + return NULL; + } + + while (*inValue && (unsigned char)*inValue <= 32) { + inValue++; + } + + return inValue; + } + + const char* Json::parseValue(Json *item, const char* value) { + /* Referenced by constructor, parseArray(), and parseObject(). */ + /* Always called with the result of skip(). */ +#if SPINE_JSON_DEBUG /* Checked at entry to graph, constructor, and after every parse call. */ + if (!value) { + /* Fail on null. */ + return NULL; + } +#endif + + switch (*value) { + case 'n': { + if (!strncmp(value + 1, "ull", 3)) { + item->_type = JSON_NULL; + return value + 4; + } + break; + } + case 'f': { + if (!strncmp(value + 1, "alse", 4)) { + item->_type = JSON_FALSE; + /* calloc prevents us needing item->_type = JSON_FALSE or valueInt = 0 here */ + return value + 5; + } + break; + } + case 't': { + if (!strncmp(value + 1, "rue", 3)) { + item->_type = JSON_TRUE; + item->_valueInt = 1; + return value + 4; + } + break; + } + case '\"': + return parseString(item, value); + case '[': + return parseArray(item, value); + case '{': + return parseObject(item, value); + case '-': /* fallthrough */ + case '0': /* fallthrough */ + case '1': /* fallthrough */ + case '2': /* fallthrough */ + case '3': /* fallthrough */ + case '4': /* fallthrough */ + case '5': /* fallthrough */ + case '6': /* fallthrough */ + case '7': /* fallthrough */ + case '8': /* fallthrough */ + case '9': + return parseNumber(item, value); + default: + break; + } + + _error = value; + return NULL; /* failure. */ + } + + static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; + const char* Json::parseString(Json *item, const char* str) { + const char* ptr = str + 1; + char* ptr2; + char* out; + int len = 0; + unsigned uc, uc2; + if (*str != '\"') { + /* TODO: don't need this check when called from parseValue, but do need from parseObject */ + _error = str; + return 0; + } /* not a string! */ + + while (*ptr != '\"' && *ptr && ++len) { + if (*ptr++ == '\\') { + ptr++; /* Skip escaped quotes. */ + } + } + + out = MALLOC(char, len + 1); /* The length needed for the string, roughly. */ + if (!out) { + return 0; + } + + ptr = str + 1; + ptr2 = out; + while (*ptr != '\"' && *ptr) { + if (*ptr != '\\') { + *ptr2++ = *ptr++; + } + else { + ptr++; + switch (*ptr) { + case 'b': + *ptr2++ = '\b'; + break; + case 'f': + *ptr2++ = '\f'; + break; + case 'n': + *ptr2++ = '\n'; + break; + case 'r': + *ptr2++ = '\r'; + break; + case 't': + *ptr2++ = '\t'; + break; + case 'u': { + /* transcode utf16 to utf8. */ + sscanf(ptr + 1, "%4x", &uc); + ptr += 4; /* get the unicode char. */ + + if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) { + break; /* check for invalid. */ + } + + /* TODO provide an option to ignore surrogates, use unicode replacement character? */ + if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */ { + if (ptr[1] != '\\' || ptr[2] != 'u') { + break; /* missing second-half of surrogate. */ + } + sscanf(ptr + 3, "%4x", &uc2); + ptr += 6; + if (uc2 < 0xDC00 || uc2 > 0xDFFF) { + break; /* invalid second-half of surrogate. */ + } + uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); + } + + len = 4; + if (uc < 0x80) { + len = 1; + } + else if (uc < 0x800) { + len = 2; + } + else if (uc < 0x10000) { + len = 3; + } + ptr2 += len; + + switch (len) { + case 4: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + /* fallthrough */ + case 3: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + /* fallthrough */ + case 2: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + /* fallthrough */ + case 1: + *--ptr2 = (uc | firstByteMark[len]); + } + ptr2 += len; + break; + } + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + + *ptr2 = NULL; + + if (*ptr == '\"') { + ptr++; /* TODO error handling if not \" or \0 ? */ + } + + item->_valueString = out; + item->_type = JSON_STRING; + + return ptr; + } + + const char* Json::parseNumber(Json *item, const char* num) { + double result = 0.0; + int negative = 0; + char* ptr = (char*)num; + + if (*ptr == '-') { + negative = -1; + ++ptr; + } + + while (*ptr >= '0' && *ptr <= '9') { + result = result * 10.0 + (*ptr - '0'); + ++ptr; + } + + if (*ptr == '.') { + double fraction = 0.0; + int n = 0; + ++ptr; + + while (*ptr >= '0' && *ptr <= '9') { + fraction = (fraction * 10.0) + (*ptr - '0'); + ++ptr; + ++n; + } + result += fraction / pow(10.0, n); + } + + if (negative) { + result = -result; + } + + if (*ptr == 'e' || *ptr == 'E') { + double exponent = 0; + int expNegative = 0; + int n = 0; + ++ptr; + + if (*ptr == '-') { + expNegative = -1; + ++ptr; + } + else if (*ptr == '+') { + ++ptr; + } + + while (*ptr >= '0' && *ptr <= '9') { + exponent = (exponent * 10.0) + (*ptr - '0'); + ++ptr; + ++n; + } + + if (expNegative) { + result = result / pow(10, exponent); + } + else { + result = result * pow(10, exponent); + } + } + + if (ptr != num) { + /* Parse success, number found. */ + item->_valueFloat = result; + item->_valueInt = static_cast(result); + item->_type = JSON_NUMBER; + return ptr; + } + else { + /* Parse failure, _error is set. */ + _error = num; + return NULL; + } + } + + const char* Json::parseArray(Json *item, const char* value) { + Json *child; + +#if SPINE_JSON_DEBUG /* unnecessary, only callsite (parse_value) verifies this */ + if (*value != '[') { + ep = value; + return 0; + } /* not an array! */ +#endif + + item->_type = JSON_ARRAY; + value = skip(value + 1); + if (*value == ']') { + return value + 1; /* empty array. */ + } + + item->_child = child = NEW(Json); + new (item->_child) Json(NULL); + if (!item->_child) { + return NULL; /* memory fail */ + } + + value = skip(parseValue(child, skip(value))); /* skip any spacing, get the value. */ + + if (!value) { + return NULL; + } + + item->_size = 1; + + while (*value == ',') { + Json *new_item = NEW(Json); + new (new_item) Json(NULL); + if (!new_item) { + return NULL; /* memory fail */ + } + child->_next = new_item; +#if SPINE_JSON_HAVE_PREV + new_item->prev = child; +#endif + child = new_item; + value = skip(parseValue(child, skip(value + 1))); + if (!value) { + return NULL; /* parse fail */ + } + item->_size++; + } + + if (*value == ']') { + return value + 1; /* end of array */ + } + + _error = value; + + return NULL; /* malformed. */ + } + + /* Build an object from the text. */ + const char* Json::parseObject(Json *item, const char* value) { + Json *child; + +#if SPINE_JSON_DEBUG /* unnecessary, only callsite (parse_value) verifies this */ + if (*value != '{') { + ep = value; + return 0; + } /* not an object! */ +#endif + + item->_type = JSON_OBJECT; + value = skip(value + 1); + if (*value == '}') { + return value + 1; /* empty array. */ + } + + item->_child = child = NEW(Json); + new (item->_child) Json(NULL); + if (!item->_child) { + return NULL; + } + value = skip(parseString(child, skip(value))); + if (!value) { + return NULL; + } + child->_name = child->_valueString; + child->_valueString = 0; + if (*value != ':') { + _error = value; + return NULL; + } /* fail! */ + + value = skip(parseValue(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) { + return NULL; + } + + item->_size = 1; + + while (*value == ',') { + Json *new_item = NEW(Json); + new (new_item) Json(NULL); + if (!new_item) { + return NULL; /* memory fail */ + } + child->_next = new_item; +#if SPINE_JSON_HAVE_PREV + new_item->prev = child; +#endif + child = new_item; + value = skip(parseString(child, skip(value + 1))); + if (!value) { + return NULL; + } + child->_name = child->_valueString; + child->_valueString = 0; + if (*value != ':') { + _error = value; + return NULL; + } /* fail! */ + + value = skip(parseValue(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) { + return NULL; + } + item->_size++; + } + + if (*value == '}') { + return value + 1; /* end of array */ + } + + _error = value; + + return NULL; /* malformed. */ + } + + int Json::json_strcasecmp(const char* s1, const char* s2) { + /* TODO we may be able to elide these NULL checks if we can prove + * the graph and input (only callsite is Json_getItem) should not have NULLs + */ + if (s1 && s2) { +#if defined(_WIN32) + return _stricmp(s1, s2); +#else + return strcasecmp( s1, s2 ); +#endif + } + else { + if (s1 < s2) { + return -1; /* s1 is null, s2 is not */ + } + else if (s1 == s2) { + return 0; /* both are null */ + } + else { + return 1; /* s2 is nul s1 is not */ + } + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/LinkedMesh.cpp b/spine-cpp/spine-cpp/src/spine/LinkedMesh.cpp new file mode 100644 index 000000000..2cbfef3d5 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/LinkedMesh.cpp @@ -0,0 +1,43 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + LinkedMesh::LinkedMesh(MeshAttachment* mesh, std::string skin, int slotIndex, std::string parent) : + _mesh(mesh), + _skin(skin), + _slotIndex(slotIndex), + _parent(parent) { + // Empty + } +} diff --git a/spine-cpp/spine-cpp/src/spine/MathUtil.cpp b/spine-cpp/spine-cpp/src/spine/MathUtil.cpp new file mode 100644 index 000000000..399365063 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/MathUtil.cpp @@ -0,0 +1,96 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +namespace Spine { + float MathUtil::SIN_TABLE[SIN_COUNT] = {0.0f}; + + MathUtil::MathUtil() { + for (int i = 0; i < SIN_COUNT; ++i) { + SIN_TABLE[i] = (float)sin((i + 0.5f) / SIN_COUNT * RadFull); + } + + for (int i = 0; i < 360; i += 90) { + SIN_TABLE[(int)(i * DegToIndex) & SIN_MASK] = (float)sin(i * DegRad); + } + } + + /// Returns the sine in radians from a lookup table. + float MathUtil::sin(float radians) { + return SIN_TABLE[(int)(radians * RadToIndex) & SIN_MASK]; + } + + /// Returns the cosine in radians from a lookup table. + float MathUtil::cos(float radians) { + return SIN_TABLE[(int)((radians + SPINE_PI / 2) * RadToIndex) & SIN_MASK]; + } + + /// Returns the sine in radians from a lookup table. + float MathUtil::sinDeg(float degrees) { + return SIN_TABLE[(int)(degrees * DegToIndex) & SIN_MASK]; + } + + /// Returns the cosine in radians from a lookup table. + float MathUtil::cosDeg(float degrees) { + return SIN_TABLE[(int)((degrees + 90) * DegToIndex) & SIN_MASK]; + } + + /// Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323 + /// degrees), largest error of 0.00488 radians (0.2796 degrees). + float MathUtil::atan2(float y, float x) { + if (areFloatsPracticallyEqual(x, 0.0f)) { + if (y > 0.0f) { + return SPINE_PI / 2; + } + + if (areFloatsPracticallyEqual(y, 0.0f)) { + return 0.0f; + } + + return -SPINE_PI / 2; + } + + float atan, z = y / x; + + if (fabs(z) < 1.0f) { + atan = z / (1.0f + 0.28f * z * z); + if (x < 0.0f) { + return atan + (y < 0.0f ? -SPINE_PI : SPINE_PI); + } + + return atan; + } + + atan = SPINE_PI / 2 - z / (z * z + 0.28f); + + return y < 0.0f ? atan - SPINE_PI : atan; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/MeshAttachment.cpp b/spine-cpp/spine-cpp/src/spine/MeshAttachment.cpp new file mode 100644 index 000000000..b6c5ff92a --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/MeshAttachment.cpp @@ -0,0 +1,305 @@ + /****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +namespace Spine { + RTTI_IMPL(MeshAttachment, VertexAttachment); + + MeshAttachment::MeshAttachment(std::string name) : VertexAttachment(name), + _regionOffsetX(0), + _regionOffsetY(0), + _regionWidth(0), + _regionHeight(0), + _regionOriginalWidth(0), + _regionOriginalHeight(0), + _parentMesh(NULL), + _rendererObject(NULL), + _path(), + _regionU(0), + _regionV(0), + _regionU2(0), + _regionV2(0), + _width(0), + _height(0), + _r(1), + _g(1), + _b(1), + _a(1), + _hullLength(0), + _inheritDeform(false), + _regionRotate(false) { + // Empty + } + + void MeshAttachment::updateUVs() { + float u = _regionU, v = _regionV, width = _regionU2 - _regionU, height = _regionV2 - _regionV; + if (_uvs.size() != _regionUVs.size()) { + _uvs.reserve(_regionUVs.size()); + _uvs.setSize(_regionUVs.size()); + } + + if (_regionRotate) { + for (size_t i = 0, n = _uvs.size(); i < n; i += 2) { + _uvs[i] = u + _regionUVs[i + 1] * width; + _uvs[i + 1] = v + height - _regionUVs[i] * height; + } + } + else { + for (size_t i = 0, n = _uvs.size(); i < n; i += 2) { + _uvs[i] = u + _regionUVs[i] * width; + _uvs[i + 1] = v + _regionUVs[i + 1] * height; + } + } + } + + bool MeshAttachment::applyDeform(VertexAttachment* sourceAttachment) { + return this == sourceAttachment || (_inheritDeform && _parentMesh == sourceAttachment); + } + + int MeshAttachment::getHullLength() { + return _hullLength; + } + + void MeshAttachment::setHullLength(float inValue) { + _hullLength = inValue; + } + + Vector& MeshAttachment::getRegionUVs() { + return _regionUVs; + } + + void MeshAttachment::setRegionUVs(Vector& inValue) { + _regionUVs = inValue; + } + + Vector& MeshAttachment::getUVs() { + return _uvs; + } + + void MeshAttachment::setUVs(Vector& inValue) { + _uvs = inValue; + } + + Vector& MeshAttachment::getTriangles() { + return _triangles; + } + + void MeshAttachment::setTriangles(Vector& inValue) { + _triangles = inValue; + } + + float MeshAttachment::getR() { + return _r; + } + + void MeshAttachment::setR(float inValue) { + _r = inValue; + } + + float MeshAttachment::getG() { + return _g; + } + + void MeshAttachment::setG(float inValue) { + _g = inValue; + } + + float MeshAttachment::getB() { + return _b; + } + + void MeshAttachment::setB(float inValue) { + _b = inValue; + } + + float MeshAttachment::getA() { + return _a; + } + + void MeshAttachment::setA(float inValue) { + _a = inValue; + } + + std::string MeshAttachment::getPath() { + return _path; + } + + void MeshAttachment::setPath(std::string inValue) { + _path = inValue; + } + + void* MeshAttachment::getRendererObject() { + return _rendererObject; + } + + void MeshAttachment::setRendererObject(void* inValue) { + _rendererObject = inValue; + } + + float MeshAttachment::getRegionU() { + return _regionU; + } + + void MeshAttachment::setRegionU(float inValue) { + _regionU = inValue; + } + + float MeshAttachment::getRegionV() { + return _regionV; + } + + void MeshAttachment::setRegionV(float inValue) { + _regionV = inValue; + } + + float MeshAttachment::getRegionU2() { + return _regionU2; + } + + void MeshAttachment::setRegionU2(float inValue) { + _regionU2 = inValue; + } + + float MeshAttachment::getRegionV2() { + return _regionV2; + } + + void MeshAttachment::setRegionV2(float inValue) { + _regionV2 = inValue; + } + + bool MeshAttachment::getRegionRotate() { + return _regionRotate; + } + + void MeshAttachment::setRegionRotate(float inValue) { + _regionRotate = inValue; + } + + float MeshAttachment::getRegionOffsetX() { + return _regionOffsetX; + } + + void MeshAttachment::setRegionOffsetX(float inValue) { + _regionOffsetX = inValue; + } + + float MeshAttachment::getRegionOffsetY() { + return _regionOffsetY; + } + + void MeshAttachment::setRegionOffsetY(float inValue) { + _regionOffsetY = inValue; + } + + float MeshAttachment::getRegionWidth() { + return _regionWidth; + } + + void MeshAttachment::setRegionWidth(float inValue) { + _regionWidth = inValue; + } + + float MeshAttachment::getRegionHeight() { + return _regionHeight; + } + + void MeshAttachment::setRegionHeight(float inValue) { + _regionHeight = inValue; + } + + float MeshAttachment::getRegionOriginalWidth() { + return _regionOriginalWidth; + } + + void MeshAttachment::setRegionOriginalWidth(float inValue) { + _regionOriginalWidth = inValue; + } + + float MeshAttachment::getRegionOriginalHeight() { + return _regionOriginalHeight; + } + + void MeshAttachment::setRegionOriginalHeight(float inValue) { + _regionOriginalHeight = inValue; + } + + bool MeshAttachment::getInheritDeform() { + return _inheritDeform; + } + + void MeshAttachment::setInheritDeform(bool inValue) { + _inheritDeform = inValue; + } + + MeshAttachment* MeshAttachment::getParentMesh() { + return _parentMesh; + } + + void MeshAttachment::setParentMesh(MeshAttachment* inValue) { + _parentMesh = inValue; + if (inValue != NULL) { + _bones = inValue->_bones; + _vertices = inValue->_vertices; + _worldVerticesLength = inValue->_worldVerticesLength; + _regionUVs = inValue->_regionUVs; + _triangles = inValue->_triangles; + _hullLength = inValue->_hullLength; + _edges = inValue->_edges; + _width = inValue->_width; + _height = inValue->_height; + } + } + + Vector& MeshAttachment::getEdges() { + return _edges; + } + + void MeshAttachment::setEdges(Vector& inValue) { + _edges = inValue; + } + + float MeshAttachment::getWidth() { + return _width; + } + + void MeshAttachment::setWidth(float inValue) { + _width = inValue; + } + + float MeshAttachment::getHeight() { + return _height; + } + + void MeshAttachment::setHeight(float inValue) { + _height = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/PathAttachment.cpp b/spine-cpp/spine-cpp/src/spine/PathAttachment.cpp new file mode 100644 index 000000000..4e5c93cfd --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/PathAttachment.cpp @@ -0,0 +1,63 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +namespace Spine { + RTTI_IMPL(PathAttachment, VertexAttachment); + + PathAttachment::PathAttachment(std::string name) : VertexAttachment(name), _closed(false), _constantSpeed(false) { + // Empty + } + + Vector& PathAttachment::getLengths() { + return _lengths; + } + + void PathAttachment::setLengths(Vector inValue) { + _lengths = inValue; + } + + bool PathAttachment::isClosed() { + return _closed; + } + + void PathAttachment::setClosed(bool inValue) { + _closed = inValue; + } + + bool PathAttachment::isConstantSpeed() { + return _constantSpeed; + } + + void PathAttachment::setConstantSpeed(bool inValue) { + _constantSpeed = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/PathConstraint.cpp b/spine-cpp/spine-cpp/src/spine/PathConstraint.cpp new file mode 100644 index 000000000..29a8882bf --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/PathConstraint.cpp @@ -0,0 +1,569 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(PathConstraint, Constraint); + + const float PathConstraint::EPSILON = 0.00001f; + const int PathConstraint::NONE = -1; + const int PathConstraint::BEFORE = -2; + const int PathConstraint::AFTER = -3; + + PathConstraint::PathConstraint(PathConstraintData& data, Skeleton& skeleton) : Constraint(), + _data(data), + _target(skeleton.findSlot(data.getTarget()->getName())), + _position(data.getPosition()), + _spacing(data.getSpacing()), + _rotateMix(data.getRotateMix()), + _translateMix(data.getTranslateMix()) { + _bones.reserve(_data.getBones().size()); + for (BoneData** i = _data.getBones().begin(); i != _data.getBones().end(); ++i) { + BoneData* boneData = (*i); + + _bones.push_back(skeleton.findBone(boneData->getName())); + } + + _segments.reserve(10); + _segments.setSize(10); + } + + void PathConstraint::apply() { + update(); + } + + void PathConstraint::update() { + Attachment* baseAttachment = _target->getAttachment(); + if (baseAttachment == NULL || !baseAttachment->getRTTI().derivesFrom(PathAttachment::rtti)) { + return; + } + + PathAttachment* attachment = static_cast(baseAttachment); + + float rotateMix = _rotateMix; + float translateMix = _translateMix; + bool translate = translateMix > 0; + bool rotate = rotateMix > 0; + if (!translate && !rotate) { + return; + } + + PathConstraintData data = _data; + SpacingMode spacingMode = data._spacingMode; + bool lengthSpacing = spacingMode == SpacingMode_Length; + RotateMode rotateMode = data._rotateMode; + bool tangents = rotateMode == RotateMode_Tangent, scale = rotateMode == RotateMode_ChainScale; + size_t boneCount = _bones.size(); + int spacesCount = static_cast(tangents ? boneCount : boneCount + 1); + _spaces.reserve(spacesCount); + _spaces.setSize(spacesCount); + float spacing = _spacing; + if (scale || lengthSpacing) { + if (scale) { + _lengths.reserve(boneCount); + _lengths.setSize(boneCount); + } + + for (int i = 0, n = spacesCount - 1; i < n;) { + Bone* boneP = _bones[i]; + Bone& bone = *boneP; + float setupLength = bone._data.getLength(); + if (setupLength < PathConstraint::EPSILON) { + if (scale) { + _lengths[i] = 0; + } + _spaces[++i] = 0; + } + else { + float x = setupLength * bone._a; + float y = setupLength * bone._c; + float length = (float)sqrt(x * x + y * y); + if (scale) { + _lengths[i] = length; + } + + _spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; + } + } + } + else { + for (int i = 1; i < spacesCount; ++i) { + _spaces[i] = spacing; + } + } + + Vector positions = computeWorldPositions(*attachment, spacesCount, tangents, data.getPositionMode() == PositionMode_Percent, spacingMode == SpacingMode_Percent); + float boneX = positions[0]; + float boneY = positions[1]; + float offsetRotation = data.getOffsetRotation(); + bool tip; + if (offsetRotation == 0) { + tip = rotateMode == RotateMode_Chain; + } + else { + tip = false; + Bone p = _target->getBone(); + offsetRotation *= p.getA() * p.getD() - p.getB() * p.getC() > 0 ? DegRad : -DegRad; + } + + for (int i = 0, p = 3; i < boneCount; i++, p += 3) { + Bone* boneP = _bones[i]; + Bone& bone = *boneP; + bone._worldX += (boneX - bone._worldX) * translateMix; + bone._worldY += (boneY - bone._worldY) * translateMix; + float x = positions[p]; + float y = positions[p + 1]; + float dx = x - boneX; + float dy = y - boneY; + if (scale) { + float length = _lengths[i]; + if (length >= PathConstraint::EPSILON) { + float s = ((float)sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; + bone._a *= s; + bone._c *= s; + } + } + + boneX = x; + boneY = y; + + if (rotate) { + float a = bone._a, b = bone._b, c = bone._c, d = bone._d, r, cos, sin; + if (tangents) { + r = positions[p - 1]; + } + else if (_spaces[i + 1] < PathConstraint::EPSILON) { + r = positions[p + 2]; + } + else { + r = MathUtil::atan2(dy, dx); + } + + r -= MathUtil::atan2(c, a); + + if (tip) { + cos = MathUtil::cos(r); + sin = MathUtil::sin(r); + float length = bone._data.getLength(); + boneX += (length * (cos * a - sin * c) - dx) * rotateMix; + boneY += (length * (sin * a + cos * c) - dy) * rotateMix; + } + else { + r += offsetRotation; + } + + if (r > SPINE_PI) { + r -= SPINE_PI_2; + } + else if (r < -SPINE_PI) { + r += SPINE_PI_2; + } + + r *= rotateMix; + cos = MathUtil::cos(r); + sin = MathUtil::sin(r); + bone._a = cos * a - sin * c; + bone._b = cos * b - sin * d; + bone._c = sin * a + cos * c; + bone._d = sin * b + cos * d; + } + + bone._appliedValid = false; + } + } + + int PathConstraint::getOrder() { + return _data.getOrder(); + } + + float PathConstraint::getPosition() { + return _position; + } + + void PathConstraint::setPosition(float inValue) { + _position = inValue; + } + + float PathConstraint::getSpacing() { + return _spacing; + } + + void PathConstraint::setSpacing(float inValue) { + _spacing = inValue; + } + + float PathConstraint::getRotateMix() { + return _rotateMix; + } + + void PathConstraint::setRotateMix(float inValue) { + _rotateMix = inValue; + } + + float PathConstraint::getTranslateMix() { + return _translateMix; + } + + void PathConstraint::setTranslateMix(float inValue) { + _translateMix = inValue; + } + + Vector& PathConstraint::getBones() { + return _bones; + } + + Slot* PathConstraint::getTarget() { + return _target; + } + + void PathConstraint::setTarget(Slot* inValue) { + _target = inValue; + } + + PathConstraintData& PathConstraint::getData() { + return _data; + } + + Vector PathConstraint::computeWorldPositions(PathAttachment& path, int spacesCount, bool tangents, bool percentPosition, bool percentSpacing) { + Slot& target = *_target; + float position = _position; + _positions.reserve(spacesCount * 3 + 2); + _positions.setSize(spacesCount * 3 + 2); + bool closed = path.isClosed(); + int verticesLength = path.getWorldVerticesLength(); + int curveCount = verticesLength / 6; + int prevCurve = NONE; + + float pathLength; + if (!path.isConstantSpeed()) { + Vector& lengths = path.getLengths(); + curveCount -= closed ? 1 : 2; + pathLength = lengths[curveCount]; + if (percentPosition) { + position *= pathLength; + } + + if (percentSpacing) { + for (int i = 0; i < spacesCount; ++i) { + _spaces[i] *= pathLength; + } + } + + _world.reserve(8); + _world.setSize(8); + for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { + float space = _spaces[i]; + position += space; + float p = position; + + if (closed) { + p = fmod(p, pathLength); + + if (p < 0) { + p += pathLength; + } + curve = 0; + } + else if (p < 0) { + if (prevCurve != BEFORE) { + prevCurve = BEFORE; + path.computeWorldVertices(target, 2, 4, _world, 0); + } + + addBeforePosition(p, _world, 0, _positions, o); + + continue; + } + else if (p > pathLength) { + if (prevCurve != AFTER) { + prevCurve = AFTER; + path.computeWorldVertices(target, verticesLength - 6, 4, _world, 0); + } + + addAfterPosition(p - pathLength, _world, 0, _positions, o); + + continue; + } + + // Determine curve containing position. + for (;; curve++) { + float length = lengths[curve]; + if (p > length) { + continue; + } + + if (curve == 0) { + p /= length; + } + else { + float prev = lengths[curve - 1]; + p = (p - prev) / (length - prev); + } + break; + } + + if (curve != prevCurve) { + prevCurve = curve; + if (closed && curve == curveCount) { + path.computeWorldVertices(target, verticesLength - 4, 4, _world, 0); + path.computeWorldVertices(target, 0, 4, _world, 4); + } + else { + path.computeWorldVertices(target, curve * 6 + 2, 8, _world, 0); + } + } + + addCurvePosition(p, _world[0], _world[1], _world[2], _world[3], _world[4], _world[5], _world[6], _world[7], _positions, o, tangents || (i > 0 && space < EPSILON)); + } + return _world; + } + + // World vertices. + if (closed) { + verticesLength += 2; + _world.reserve(verticesLength); + _world.setSize(verticesLength); + path.computeWorldVertices(target, 2, verticesLength - 4, _world, 0); + path.computeWorldVertices(target, 0, 2, _world, verticesLength - 4); + _world[verticesLength - 2] = _world[0]; + _world[verticesLength - 1] = _world[1]; + } + else { + curveCount--; + verticesLength -= 4; + _world.reserve(verticesLength); + _world.setSize(verticesLength); + path.computeWorldVertices(target, 2, verticesLength, _world, 0); + } + + // Curve lengths. + _curves.reserve(curveCount); + _curves.setSize(curveCount); + pathLength = 0; + float x1 = _world[0], y1 = _world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; + float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy; + for (int i = 0, w = 2; i < curveCount; i++, w += 6) { + cx1 = _world[w]; + cy1 = _world[w + 1]; + cx2 = _world[w + 2]; + cy2 = _world[w + 3]; + x2 = _world[w + 4]; + y2 = _world[w + 5]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f; + pathLength += (float)sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + pathLength += (float)sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + pathLength += (float)sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + pathLength += (float)sqrt(dfx * dfx + dfy * dfy); + _curves[i] = pathLength; + x1 = x2; + y1 = y2; + } + + if (percentPosition) { + position *= pathLength; + } + + if (percentSpacing) { + for (int i = 0; i < spacesCount; ++i) { + _spaces[i] *= pathLength; + } + } + + float curveLength = 0; + for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { + float space = _spaces[i]; + position += space; + float p = position; + + if (closed) { + p = fmod(p, pathLength); + + if (p < 0) { + p += pathLength; + } + curve = 0; + } + else if (p < 0) { + addBeforePosition(p, _world, 0, _positions, o); + continue; + } + else if (p > pathLength) { + addAfterPosition(p - pathLength, _world, verticesLength - 4, _positions, o); + continue; + } + + // Determine curve containing position. + for (;; curve++) { + float length = _curves[curve]; + if (p > length) { + continue; + } + + if (curve == 0) { + p /= length; + } + else { + float prev = _curves[curve - 1]; + p = (p - prev) / (length - prev); + } + break; + } + + // Curve segment lengths. + if (curve != prevCurve) { + prevCurve = curve; + int ii = curve * 6; + x1 = _world[ii]; + y1 = _world[ii + 1]; + cx1 = _world[ii + 2]; + cy1 = _world[ii + 3]; + cx2 = _world[ii + 4]; + cy2 = _world[ii + 5]; + x2 = _world[ii + 6]; + y2 = _world[ii + 7]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.03f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; + curveLength = (float)sqrt(dfx * dfx + dfy * dfy); + _segments[0] = curveLength; + for (ii = 1; ii < 8; ii++) { + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + curveLength += (float)sqrt(dfx * dfx + dfy * dfy); + _segments[ii] = curveLength; + } + dfx += ddfx; + dfy += ddfy; + curveLength += (float)sqrt(dfx * dfx + dfy * dfy); + _segments[8] = curveLength; + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + curveLength += (float)sqrt(dfx * dfx + dfy * dfy); + _segments[9] = curveLength; + segment = 0; + } + + // Weight by segment length. + p *= curveLength; + for (;; segment++) { + float length = _segments[segment]; + if (p > length) { + continue; + } + + if (segment == 0) { + p /= length; + } + else { + float prev = _segments[segment - 1]; + p = segment + (p - prev) / (length - prev); + } + break; + } + addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, _positions, o, tangents || (i > 0 && space < EPSILON)); + } + + return _positions; + } + + void PathConstraint::addBeforePosition(float p, Vector& temp, int i, Vector& output, int o) { + float x1 = temp[i]; + float y1 = temp[i + 1]; + float dx = temp[i + 2] - x1; + float dy = temp[i + 3] - y1; + float r = MathUtil::atan2(dy, dx); + + output[o] = x1 + p * MathUtil::cos(r); + output[o + 1] = y1 + p * MathUtil::sin(r); + output[o + 2] = r; + } + + void PathConstraint::addAfterPosition(float p, Vector& temp, int i, Vector& output, int o) { + float x1 = temp[i + 2]; + float y1 = temp[i + 3]; + float dx = x1 - temp[i]; + float dy = y1 - temp[i + 1]; + float r = MathUtil::atan2(dy, dx); + output[o] = x1 + p * MathUtil::cos(r); + output[o + 1] = y1 + p * MathUtil::sin(r); + output[o + 2] = r; + } + + void PathConstraint::addCurvePosition(float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, Vector& output, int o, bool tangents) { + if (p < EPSILON || isnan(p)) { + p = EPSILON; + } + + float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; + float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; + float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + output[o] = x; + output[o + 1] = y; + if (tangents) { + output[o + 2] = (float)atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/PathConstraintData.cpp b/spine-cpp/spine-cpp/src/spine/PathConstraintData.cpp new file mode 100644 index 000000000..caa545e3d --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/PathConstraintData.cpp @@ -0,0 +1,141 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include + +namespace Spine { + PathConstraintData::PathConstraintData(std::string name) : + _name(name), + _order(0), + _target(NULL), + _positionMode(PositionMode_Fixed), + _spacingMode(SpacingMode_Length), + _rotateMode(RotateMode_Tangent), + _offsetRotation(0), + _position(0), + _spacing(0), + _rotateMix(0), + _translateMix(0) { + assert(_name.length() > 0); + } + + const std::string& PathConstraintData::getName() { + return _name; + } + + int PathConstraintData::getOrder() { + return _order; + } + + void PathConstraintData::setOrder(int inValue) { + _order = inValue; + } + + Vector& PathConstraintData::getBones() { + return _bones; + } + + SlotData* PathConstraintData::getTarget() { + return _target; + } + + void PathConstraintData::setTarget(SlotData* inValue) { + _target = inValue; + } + + PositionMode PathConstraintData::getPositionMode() { + return _positionMode; + } + + void PathConstraintData::setPositionMode(PositionMode inValue) { + _positionMode = inValue; + } + + SpacingMode PathConstraintData::getSpacingMode() { + return _spacingMode; + } + + void PathConstraintData::setSpacingMode(SpacingMode inValue) { + _spacingMode = inValue; + } + + RotateMode PathConstraintData::getRotateMode() { + return _rotateMode; + } + + void PathConstraintData::setRotateMode(RotateMode inValue) { + _rotateMode = inValue; + } + + float PathConstraintData::getOffsetRotation() { + return _offsetRotation; + } + + void PathConstraintData::setOffsetRotation(float inValue) { + _offsetRotation = inValue; + } + + float PathConstraintData::getPosition() { + return _position; + } + + void PathConstraintData::setPosition(float inValue) { + _position = inValue; + } + + float PathConstraintData::getSpacing() { + return _spacing; + } + + void PathConstraintData::setSpacing(float inValue) { + _spacing = inValue; + } + + float PathConstraintData::getRotateMix() { + return _rotateMix; + } + + void PathConstraintData::setRotateMix(float inValue) { + _rotateMix = inValue; + } + + float PathConstraintData::getTranslateMix() { + return _translateMix; + } + + void PathConstraintData::setTranslateMix(float inValue) { + _translateMix = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp b/spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp new file mode 100644 index 000000000..556ef1f37 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp @@ -0,0 +1,116 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(PathConstraintMixTimeline, CurveTimeline); + + const int PathConstraintMixTimeline::ENTRIES = 3; + const int PathConstraintMixTimeline::PREV_TIME = -3; + const int PathConstraintMixTimeline::PREV_ROTATE = -2; + const int PathConstraintMixTimeline::PREV_TRANSLATE = -1; + const int PathConstraintMixTimeline::ROTATE = 1; + const int PathConstraintMixTimeline::TRANSLATE = 2; + + PathConstraintMixTimeline::PathConstraintMixTimeline(int frameCount) : CurveTimeline(frameCount), _pathConstraintIndex(0) { + _frames.reserve(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES); + } + + void PathConstraintMixTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + PathConstraint* constraintP = skeleton._pathConstraints[_pathConstraintIndex]; + PathConstraint& constraint = *constraintP; + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + constraint._rotateMix = constraint._data._rotateMix; + constraint._translateMix = constraint._data._translateMix; + return; + case MixPose_Current: + constraint._rotateMix += (constraint._data._rotateMix - constraint._rotateMix) * alpha; + constraint._translateMix += (constraint._data._translateMix - constraint._translateMix) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float rotate, translate; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + rotate = _frames[_frames.size() + PREV_ROTATE]; + translate = _frames[_frames.size() + PREV_TRANSLATE]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + rotate = _frames[frame + PREV_ROTATE]; + translate = _frames[frame + PREV_TRANSLATE]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + rotate += (_frames[frame + ROTATE] - rotate) * percent; + translate += (_frames[frame + TRANSLATE] - translate) * percent; + } + + if (pose == MixPose_Setup) { + constraint._rotateMix = constraint._data._rotateMix + (rotate - constraint._data._rotateMix) * alpha; + constraint._translateMix = constraint._data._translateMix + (translate - constraint._data._translateMix) * alpha; + } + else { + constraint._rotateMix += (rotate - constraint._rotateMix) * alpha; + constraint._translateMix += (translate - constraint._translateMix) * alpha; + } + } + + int PathConstraintMixTimeline::getPropertyId() { + return ((int)TimelineType_PathConstraintMix << 24) + _pathConstraintIndex; + } + + void PathConstraintMixTimeline::setFrame(int frameIndex, float time, float rotateMix, float translateMix) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + ROTATE] = rotateMix; + _frames[frameIndex + TRANSLATE] = translateMix; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp b/spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp new file mode 100644 index 000000000..e5635d4dd --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp @@ -0,0 +1,105 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(PathConstraintPositionTimeline, CurveTimeline); + + const int PathConstraintPositionTimeline::ENTRIES = 2; + const int PathConstraintPositionTimeline::PREV_TIME = -2; + const int PathConstraintPositionTimeline::PREV_VALUE = -1; + const int PathConstraintPositionTimeline::VALUE = 1; + + PathConstraintPositionTimeline::PathConstraintPositionTimeline(int frameCount) : CurveTimeline(frameCount), _pathConstraintIndex(0) { + _frames.reserve(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES); + } + + void PathConstraintPositionTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + PathConstraint* constraintP = skeleton._pathConstraints[_pathConstraintIndex]; + PathConstraint& constraint = *constraintP; + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + constraint._position = constraint._data._position; + return; + case MixPose_Current: + constraint._position += (constraint._data._position - constraint._position) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float position; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + position = _frames[_frames.size() + PREV_VALUE]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + position = _frames[frame + PREV_VALUE]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + position += (_frames[frame + VALUE] - position) * percent; + } + if (pose == MixPose_Setup) { + constraint._position = constraint._data._position + (position - constraint._data._position) * alpha; + } + else { + constraint._position += (position - constraint._position) * alpha; + } + } + + int PathConstraintPositionTimeline::getPropertyId() { + return ((int)TimelineType_PathConstraintPosition << 24) + _pathConstraintIndex; + } + + void PathConstraintPositionTimeline::setFrame(int frameIndex, float time, float value) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + VALUE] = value; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp b/spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp new file mode 100644 index 000000000..23305353c --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp @@ -0,0 +1,94 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(PathConstraintSpacingTimeline, PathConstraintPositionTimeline); + + PathConstraintSpacingTimeline::PathConstraintSpacingTimeline(int frameCount) : PathConstraintPositionTimeline(frameCount) { + // Empty + } + + void PathConstraintSpacingTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + PathConstraint* constraintP = skeleton._pathConstraints[_pathConstraintIndex]; + PathConstraint& constraint = *constraintP; + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + constraint._spacing = constraint._data._spacing; + return; + case MixPose_Current: + constraint._spacing += (constraint._data._spacing - constraint._spacing) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float spacing; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + spacing = _frames[_frames.size() + PREV_VALUE]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + spacing = _frames[frame + PREV_VALUE]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + spacing += (_frames[frame + VALUE] - spacing) * percent; + } + + if (pose == MixPose_Setup) { + constraint._spacing = constraint._data._spacing + (spacing - constraint._data._spacing) * alpha; + } + else { + constraint._spacing += (spacing - constraint._spacing) * alpha; + } + } + + int PathConstraintSpacingTimeline::getPropertyId() { + return ((int)TimelineType_PathConstraintSpacing << 24) + _pathConstraintIndex; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/PointAttachment.cpp b/spine-cpp/spine-cpp/src/spine/PointAttachment.cpp new file mode 100644 index 000000000..d69338695 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/PointAttachment.cpp @@ -0,0 +1,80 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +#include + +namespace Spine { + RTTI_IMPL(PointAttachment, Attachment); + + PointAttachment::PointAttachment(std::string name) : Attachment(name), _x(0), _y(0), _rotation(0) { + // Empty + } + + void PointAttachment::computeWorldPosition(Bone& bone, float& ox, float& oy) { + bone.localToWorld(_x, _y, ox, oy); + } + + float PointAttachment::computeWorldRotation(Bone& bone) { + float cos = MathUtil::cosDeg(_rotation); + float sin = MathUtil::sinDeg(_rotation); + float ix = cos * bone._a + sin * bone._b; + float iy = cos * bone._c + sin * bone._d; + + return MathUtil::atan2(iy, ix) * RadDeg; + } + + float PointAttachment::getX() { + return _x; + } + + void PointAttachment::setX(float inValue) { + _x = inValue; + } + + float PointAttachment::getY() { + return _y; + } + + void PointAttachment::setY(float inValue) { + _y = inValue; + } + + float PointAttachment::getRotation() { + return _rotation; + } + + void PointAttachment::setRotation(float inValue) { + _rotation = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/RTTI.cpp b/spine-cpp/spine-cpp/src/spine/RTTI.cpp new file mode 100644 index 000000000..db6898c25 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/RTTI.cpp @@ -0,0 +1,63 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include + +namespace Spine { + RTTI::RTTI(const std::string& className) : m_className(className), m_pBaseRTTI(NULL) { + // Empty + } + + RTTI::RTTI(const std::string& className, const RTTI& baseRTTI) : m_className(className), m_pBaseRTTI(&baseRTTI) { + // Empty + } + + const std::string& RTTI::getClassName() const { + return m_className; + } + + bool RTTI::isExactly(const RTTI& rtti) const { + return (this == &rtti); + } + + bool RTTI::derivesFrom(const RTTI& rtti) const { + const RTTI * pCompare = this; + + while (pCompare) { + if (pCompare == &rtti) { + return true; + } + + pCompare = pCompare->m_pBaseRTTI; + } + + return false; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/RegionAttachment.cpp b/spine-cpp/spine-cpp/src/spine/RegionAttachment.cpp new file mode 100644 index 000000000..8a83f28ea --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/RegionAttachment.cpp @@ -0,0 +1,323 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +#include + +#include + +namespace Spine { + RTTI_IMPL(RegionAttachment, Attachment); + + const int RegionAttachment::BLX = 0; + const int RegionAttachment::BLY = 1; + const int RegionAttachment::ULX = 2; + const int RegionAttachment::ULY = 3; + const int RegionAttachment::URX = 4; + const int RegionAttachment::URY = 5; + const int RegionAttachment::BRX = 6; + const int RegionAttachment::BRY = 7; + + RegionAttachment::RegionAttachment(std::string name) : Attachment(name), + _x(0), + _y(0), + _rotation(0), + _scaleX(1), + _scaleY(1), + _width(0), + _height(0), + _regionOffsetX(0), + _regionOffsetY(0), + _regionWidth(0), + _regionHeight(0), + _regionOriginalWidth(0), + _regionOriginalHeight(0), + _rendererObject(NULL), + _path(), + _regionU(0), + _regionV(0), + _regionU2(0), + _regionV2(0), + _r(0), + _g(0), + _b(0), + _a(0) { + _offset.reserve(NUM_UVS); + _uvs.reserve(NUM_UVS); + + _offset.setSize(NUM_UVS); + _uvs.setSize(NUM_UVS); + } + + void RegionAttachment::updateOffset() { + float regionScaleX = _width / _regionOriginalWidth * _scaleX; + float regionScaleY = _height / _regionOriginalHeight * _scaleY; + float localX = -_width / 2 * _scaleX + _regionOffsetX * regionScaleX; + float localY = -_height / 2 * _scaleY + _regionOffsetY * regionScaleY; + float localX2 = localX + _regionWidth * regionScaleX; + float localY2 = localY + _regionHeight * regionScaleY; + float cos = MathUtil::cosDeg(_rotation); + float sin = MathUtil::sinDeg(_rotation); + float localXCos = localX * cos + _x; + float localXSin = localX * sin; + float localYCos = localY * cos + _y; + float localYSin = localY * sin; + float localX2Cos = localX2 * cos + _x; + float localX2Sin = localX2 * sin; + float localY2Cos = localY2 * cos + _y; + float localY2Sin = localY2 * sin; + + _offset[BLX] = localXCos - localYSin; + _offset[BLY] = localYCos + localXSin; + _offset[ULX] = localXCos - localY2Sin; + _offset[ULY] = localY2Cos + localXSin; + _offset[URX] = localX2Cos - localY2Sin; + _offset[URY] = localY2Cos + localX2Sin; + _offset[BRX] = localX2Cos - localYSin; + _offset[BRY] = localYCos + localX2Sin; + } + + void RegionAttachment::setUVs(float u, float v, float u2, float v2, bool rotate) { + if (rotate) { + _uvs[URX] = u; + _uvs[URY] = v2; + _uvs[BRX] = u; + _uvs[BRY] = v; + _uvs[BLX] = u2; + _uvs[BLY] = v; + _uvs[ULX] = u2; + _uvs[ULY] = v2; + } + else { + _uvs[ULX] = u; + _uvs[ULY] = v2; + _uvs[URX] = u; + _uvs[URY] = v; + _uvs[BRX] = u2; + _uvs[BRY] = v; + _uvs[BLX] = u2; + _uvs[BLY] = v2; + } + } + + void RegionAttachment::computeWorldVertices(Bone& bone, Vector& worldVertices, int offset, int stride) { + assert(worldVertices.size() >= (offset + 8)); + + float bwx = bone._worldX, bwy = bone._worldY; + float a = bone._a, b = bone._b, c = bone._c, d = bone._d; + float offsetX, offsetY; + + offsetX = _offset[BRX]; // 0 + offsetY = _offset[BRY]; // 1 + worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl + worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; + offset += stride; + + offsetX = _offset[BLX]; // 2 + offsetY = _offset[BLY]; // 3 + worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul + worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; + offset += stride; + + offsetX = _offset[ULX]; // 4 + offsetY = _offset[ULY]; // 5 + worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur + worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; + offset += stride; + + offsetX = _offset[URX]; // 6 + offsetY = _offset[URY]; // 7 + worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br + worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; + } + + float RegionAttachment::getX() { + return _x; + } + + void RegionAttachment::setX(float inValue) { + _x = inValue; + } + + float RegionAttachment::getY() { + return _y; + } + + void RegionAttachment::setY(float inValue) { + _y = inValue; + } + + float RegionAttachment::getRotation() { + return _rotation; + } + + void RegionAttachment::setRotation(float inValue) { + _rotation = inValue; + } + + float RegionAttachment::getScaleX() { + return _scaleX; + } + + void RegionAttachment::setScaleX(float inValue) { + _scaleX = inValue; + } + + float RegionAttachment::getScaleY() { + return _scaleY; + } + + void RegionAttachment::setScaleY(float inValue) { + _scaleY = inValue; + } + + float RegionAttachment::getWidth() { + return _width; + } + + void RegionAttachment::setWidth(float inValue) { + _width = inValue; + } + + float RegionAttachment::getHeight() { + return _height; + } + + void RegionAttachment::setHeight(float inValue) { + _height = inValue; + } + + float RegionAttachment::getR() { + return _r; + } + + void RegionAttachment::setR(float inValue) { + _r = inValue; + } + + float RegionAttachment::getG() { + return _g; + } + + void RegionAttachment::setG(float inValue) { + _g = inValue; + } + + float RegionAttachment::getB() { + return _b; + } + + void RegionAttachment::setB(float inValue) { + _b = inValue; + } + + float RegionAttachment::getA() { + return _a; + } + + void RegionAttachment::setA(float inValue) { + _a = inValue; + } + + std::string RegionAttachment::getPath() { + return _path; + } + + void RegionAttachment::setPath(std::string inValue) { + _path = inValue; + } + + void* RegionAttachment::getRendererObject() { + return _rendererObject; + } + + void RegionAttachment::setRendererObject(void* inValue) { + _rendererObject = inValue; + } + + float RegionAttachment::getRegionOffsetX() { + return _regionOffsetX; + } + + void RegionAttachment::setRegionOffsetX(float inValue) { + _regionOffsetX = inValue; + } + + float RegionAttachment::getRegionOffsetY() { + return _regionOffsetY; + } + + void RegionAttachment::setRegionOffsetY(float inValue) { + _regionOffsetY = inValue; + } + + float RegionAttachment::getRegionWidth() { + return _regionWidth; + } + + void RegionAttachment::setRegionWidth(float inValue) { + _regionWidth = inValue; + } + + float RegionAttachment::getRegionHeight() { + return _regionHeight; + } + + void RegionAttachment::setRegionHeight(float inValue) { + _regionHeight = inValue; + } + + float RegionAttachment::getRegionOriginalWidth() { + return _regionOriginalWidth; + } + + void RegionAttachment::setRegionOriginalWidth(float inValue) { + _regionOriginalWidth = inValue; + } + + float RegionAttachment::getRegionOriginalHeight() { + return _regionOriginalHeight; + } + + void RegionAttachment::setRegionOriginalHeight(float inValue) { + _regionOriginalHeight = inValue; + } + + Vector& RegionAttachment::getOffset() { + return _offset; + } + + Vector& RegionAttachment::getUVs() { + return _uvs; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp b/spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp new file mode 100644 index 000000000..cfc2866ef --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp @@ -0,0 +1,133 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(RotateTimeline, CurveTimeline); + + RotateTimeline::RotateTimeline(int frameCount) : CurveTimeline(frameCount), _boneIndex(0) { + _frames.reserve(frameCount << 1); + _frames.setSize(frameCount << 1); + } + + void RotateTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + Bone* bone = skeleton.getBones()[_boneIndex]; + + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: { + bone->_rotation = bone->_data._rotation; + break; + } + case MixPose_Current: { + float rr = bone->_data._rotation - bone->_rotation; + rr -= (16384 - (int)(16384.499999999996 - rr / 360)) * 360; + bone->_rotation += rr * alpha; + break; + } + case MixPose_CurrentLayered: { + // TODO? + break; + } + } + + return; + } + + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + if (pose == MixPose_Setup) { + bone->_rotation = bone->_data._rotation + _frames[_frames.size() + PREV_ROTATION] * alpha; + } + else { + float rr = bone->_data._rotation + _frames[_frames.size() + PREV_ROTATION] - bone->_rotation; + rr -= (16384 - (int)(16384.499999999996 - rr / 360)) * 360; // Wrap within -180 and 180. + bone->_rotation += rr * alpha; + } + + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + float prevRotation = _frames[frame + PREV_ROTATION]; + float frameTime = _frames[frame]; + float percent = getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + float r = _frames[frame + ROTATION] - prevRotation; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + r = prevRotation + r * percent; + + if (pose == MixPose_Setup) { + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + bone->_rotation = bone->_data._rotation + r * alpha; + } + else { + r = bone->_data._rotation + r - bone->_rotation; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + bone->_rotation += r * alpha; + } + } + + int RotateTimeline::getPropertyId() { + return ((int)TimelineType_Rotate << 24) + _boneIndex; + } + + void RotateTimeline::setFrame(int frameIndex, float time, float degrees) { + frameIndex <<= 1; + _frames[frameIndex] = time; + _frames[frameIndex + ROTATION] = degrees; + } + + int RotateTimeline::getBoneIndex() { + return _boneIndex; + } + + void RotateTimeline::setBoneIndex(int inValue) { + _boneIndex = inValue; + } + + Vector& RotateTimeline::getFrames() { + return _frames; + } + + void RotateTimeline::setFrames(Vector inValue) { + _frames = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp b/spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp new file mode 100644 index 000000000..211a6fdba --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp @@ -0,0 +1,120 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(ScaleTimeline, TranslateTimeline); + + ScaleTimeline::ScaleTimeline(int frameCount) : TranslateTimeline(frameCount) { + // Empty + } + + void ScaleTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + Bone* boneP = skeleton._bones[_boneIndex]; + Bone& bone = *boneP; + + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + bone._scaleX = bone._data._scaleX; + bone._scaleY = bone._data._scaleY; + return; + case MixPose_Current: + bone._scaleX += (bone._data._scaleX - bone._scaleX) * alpha; + bone._scaleY += (bone._data._scaleY - bone._scaleY) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float x, y; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + x = _frames[_frames.size() + PREV_X] * bone._data._scaleX; + y = _frames[_frames.size() + PREV_Y] * bone._data._scaleY; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + x = _frames[frame + PREV_X]; + y = _frames[frame + PREV_Y]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + x = (x + (_frames[frame + X] - x) * percent) * bone._data._scaleX; + y = (y + (_frames[frame + Y] - y) * percent) * bone._data._scaleY; + } + + if (alpha == 1) { + bone._scaleX = x; + bone._scaleY = y; + } + else { + float bx, by; + if (pose == MixPose_Setup) { + bx = bone._data._scaleX; + by = bone._data._scaleY; + } + else { + bx = bone._scaleX; + by = bone._scaleY; + } + // Mixing out uses sign of setup or current pose, else use sign of key. + if (direction == MixDirection_Out) { + x = (x >= 0 ? x : -x) * (bx >= 0 ? 1 : -1); + y = (y >= 0 ? y : -y) * (by >= 0 ? 1 : -1); + } + else { + bx = (bx >= 0 ? bx : -bx) * (x >= 0 ? 1 : -1); + by = (by >= 0 ? by : -by) * (y >= 0 ? 1 : -1); + } + bone._scaleX = bx + (x - bx) * alpha; + bone._scaleY = by + (y - by) * alpha; + } + } + + int ScaleTimeline::getPropertyId() { + return ((int)TimelineType_Scale << 24) + _boneIndex; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp b/spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp new file mode 100644 index 000000000..a4d433921 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp @@ -0,0 +1,102 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(ShearTimeline, TranslateTimeline); + + ShearTimeline::ShearTimeline(int frameCount) : TranslateTimeline(frameCount) { + // Empty + } + + void ShearTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + Bone* boneP = skeleton._bones[_boneIndex]; + Bone& bone = *boneP; + + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + bone._shearX = bone._data._shearX; + bone._shearY = bone._data._shearY; + return; + case MixPose_Current: + bone._shearX += (bone._data._shearX - bone._shearX) * alpha; + bone._shearY += (bone._data._shearY - bone._shearY) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float x, y; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + x = _frames[_frames.size() + PREV_X]; + y = _frames[_frames.size() + PREV_Y]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + x = _frames[frame + PREV_X]; + y = _frames[frame + PREV_Y]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + x = x + (_frames[frame + X] - x) * percent; + y = y + (_frames[frame + Y] - y) * percent; + } + + if (pose == MixPose_Setup) { + bone._shearX = bone._data._shearX + x * alpha; + bone._shearY = bone._data._shearY + y * alpha; + } + else { + bone._shearX += (bone._data._shearX + x - bone._shearX) * alpha; + bone._shearY += (bone._data._shearY + y - bone._shearY) * alpha; + } + } + + int ShearTimeline::getPropertyId() { + return ((int)TimelineType_Shear << 24) + _boneIndex; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Skeleton.cpp b/spine-cpp/spine-cpp/src/spine/Skeleton.cpp new file mode 100644 index 000000000..221298519 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Skeleton.cpp @@ -0,0 +1,723 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace Spine { + Skeleton::Skeleton(SkeletonData& skeletonData) : + _data(skeletonData), + _skin(NULL), + _r(1), + _g(1), + _b(1), + _a(1), + _time(0), + _flipX(false), + _flipY(false), + _x(0), + _y(0) { + _bones.reserve(_data.getBones().size()); + for (BoneData** i = _data.getBones().begin(); i != _data.getBones().end(); ++i) { + BoneData* data = (*i); + + Bone* bone; + if (data->getParent() == NULL) { + bone = NEW(Bone); + new (bone) Bone(*data, *this, NULL); + } + else { + Bone* parent = _bones[data->getParent()->getIndex()]; + bone = NEW(Bone); + new (bone) Bone(*data, *this, parent); + parent->getChildren().push_back(bone); + } + + _bones.push_back(bone); + } + + _slots.reserve(_data.getSlots().size()); + _drawOrder.reserve(_data.getSlots().size()); + for (SlotData** i = _data.getSlots().begin(); i != _data.getSlots().end(); ++i) { + SlotData* data = (*i); + + Bone* bone = _bones[data->getBoneData().getIndex()]; + Slot* slot = NEW(Slot); + new (slot) Slot(*data, *bone); + + _slots.push_back(slot); + _drawOrder.push_back(slot); + } + + _ikConstraints.reserve(_data.getIkConstraints().size()); + for (IkConstraintData** i = _data.getIkConstraints().begin(); i != _data.getIkConstraints().end(); ++i) { + IkConstraintData* data = (*i); + + IkConstraint* constraint = NEW(IkConstraint); + new (constraint) IkConstraint(*data, *this); + + _ikConstraints.push_back(constraint); + } + + _transformConstraints.reserve(_data.getTransformConstraints().size()); + for (TransformConstraintData** i = _data.getTransformConstraints().begin(); i != _data.getTransformConstraints().end(); ++i) { + TransformConstraintData* data = (*i); + + TransformConstraint* constraint = NEW(TransformConstraint); + new (constraint) TransformConstraint(*data, *this); + + _transformConstraints.push_back(constraint); + } + + _pathConstraints.reserve(_data.getPathConstraints().size()); + for (PathConstraintData** i = _data.getPathConstraints().begin(); i != _data.getPathConstraints().end(); ++i) { + PathConstraintData* data = (*i); + + PathConstraint* constraint = NEW(PathConstraint); + new (constraint) PathConstraint(*data, *this); + + _pathConstraints.push_back(constraint); + } + + updateCache(); + updateWorldTransform(); + } + + Skeleton::~Skeleton() { + ContainerUtil::cleanUpVectorOfPointers(_bones); + ContainerUtil::cleanUpVectorOfPointers(_slots); + ContainerUtil::cleanUpVectorOfPointers(_ikConstraints); + ContainerUtil::cleanUpVectorOfPointers(_transformConstraints); + ContainerUtil::cleanUpVectorOfPointers(_pathConstraints); + } + + void Skeleton::updateCache() { + _updateCache.clear(); + _updateCacheReset.clear(); + + for (int i = 0, n = static_cast(_bones.size()); i < n; ++i) { + _bones[i]->_sorted = false; + } + + int ikCount = static_cast(_ikConstraints.size()); + int transformCount = static_cast(_transformConstraints.size()); + int pathCount = static_cast(_pathConstraints.size()); + + int constraintCount = ikCount + transformCount + pathCount; + + for (int i = 0; i < constraintCount; ++i) { + bool gotoNextConstraintCount = false; + + for (int ii = 0; ii < ikCount; ++ii) { + IkConstraint* constraint = _ikConstraints[ii]; + if (constraint->getData().getOrder() == i) { + sortIkConstraint(constraint); + + gotoNextConstraintCount = true; + break; + } + } + + if (gotoNextConstraintCount) { + break; + } + + for (int ii = 0; ii < transformCount; ++ii) { + TransformConstraint* constraint = _transformConstraints[ii]; + if (constraint->getData().getOrder() == i) { + sortTransformConstraint(constraint); + + gotoNextConstraintCount = true; + break; + } + } + + if (gotoNextConstraintCount) { + break; + } + + for (int ii = 0; ii < pathCount; ++ii) { + PathConstraint* constraint = _pathConstraints[ii]; + if (constraint->getData().getOrder() == i) { + sortPathConstraint(constraint); + + gotoNextConstraintCount = true; + break; + } + } + + if (gotoNextConstraintCount) { + break; + } + } + + for (int i = 0, n = static_cast(_bones.size()); i < n; ++i) { + sortBone(_bones[i]); + } + } + + void Skeleton::updateWorldTransform() { + for (int i = 0, n = static_cast(_updateCacheReset.size()); i < n; ++i) { + Bone* boneP = _updateCacheReset[i]; + Bone& bone = *boneP; + bone._ax = bone._x; + bone._ay = bone._y; + bone._arotation = bone._rotation; + bone._ascaleX = bone._scaleX; + bone._ascaleY = bone._scaleY; + bone._ashearX = bone._shearX; + bone._ashearY = bone._shearY; + bone._appliedValid = true; + } + + for (int i = 0, n = static_cast(_updateCache.size()); i < n; ++i) { + _updateCache[i]->update(); + } + } + + void Skeleton::setToSetupPose() { + setBonesToSetupPose(); + setSlotsToSetupPose(); + } + + void Skeleton::setBonesToSetupPose() { + for (int i = 0, n = static_cast(_bones.size()); i < n; ++i) { + _bones[i]->setToSetupPose(); + } + + for (int i = 0, n = static_cast(_ikConstraints.size()); i < n; ++i) { + IkConstraint* constraintP = _ikConstraints[i]; + IkConstraint& constraint = *constraintP; + + constraint._bendDirection = constraint._data._bendDirection; + constraint._mix = constraint._data._mix; + } + + for (int i = 0, n = static_cast(_transformConstraints.size()); i < n; ++i) { + TransformConstraint* constraintP = _transformConstraints[i]; + TransformConstraint& constraint = *constraintP; + TransformConstraintData& constraintData = constraint._data; + + constraint._rotateMix = constraintData._rotateMix; + constraint._translateMix = constraintData._translateMix; + constraint._scaleMix = constraintData._scaleMix; + constraint._shearMix = constraintData._shearMix; + } + + for (int i = 0, n = static_cast(_pathConstraints.size()); i < n; ++i) { + PathConstraint* constraintP = _pathConstraints[i]; + PathConstraint& constraint = *constraintP; + PathConstraintData& constraintData = constraint._data; + + constraint._position = constraintData._position; + constraint._spacing = constraintData._spacing; + constraint._rotateMix = constraintData._rotateMix; + constraint._translateMix = constraintData._translateMix; + } + } + + void Skeleton::setSlotsToSetupPose() { + _drawOrder.clear(); + for (int i = 0, n = static_cast(_slots.size()); i < n; ++i) { + _drawOrder.push_back(_slots[i]); + } + + for (int i = 0, n = static_cast(_slots.size()); i < n; ++i) { + _slots[i]->setToSetupPose(); + } + } + + Bone* Skeleton::findBone(std::string boneName) { + return ContainerUtil::findWithDataName(_bones, boneName); + } + + int Skeleton::findBoneIndex(std::string boneName) { + return ContainerUtil::findIndexWithDataName(_bones, boneName); + } + + Slot* Skeleton::findSlot(std::string slotName) { + return ContainerUtil::findWithDataName(_slots, slotName); + } + + int Skeleton::findSlotIndex(std::string slotName) { + return ContainerUtil::findIndexWithDataName(_slots, slotName); + } + + void Skeleton::setSkin(std::string skinName) { + Skin* foundSkin = _data.findSkin(skinName); + + assert(foundSkin != NULL); + + setSkin(foundSkin); + } + + void Skeleton::setSkin(Skin* newSkin) { + if (newSkin != NULL) { + if (_skin != NULL) { + Skeleton& thisRef = *this; + newSkin->attachAll(thisRef, *_skin); + } + else { + for (int i = 0, n = static_cast(_slots.size()); i < n; ++i) { + Slot* slotP = _slots[i]; + Slot& slot = *slotP; + std::string name = slot._data.getAttachmentName(); + if (name.length() > 0) { + Attachment* attachment = newSkin->getAttachment(i, name); + if (attachment != NULL) { + slot.setAttachment(attachment); + } + } + } + } + } + + _skin = newSkin; + } + + Attachment* Skeleton::getAttachment(std::string slotName, std::string attachmentName) { + return getAttachment(_data.findSlotIndex(slotName), attachmentName); + } + + Attachment* Skeleton::getAttachment(int slotIndex, std::string attachmentName) { + assert(attachmentName.length() > 0); + + if (_skin != NULL) { + Attachment* attachment = _skin->getAttachment(slotIndex, attachmentName); + if (attachment != NULL) { + return attachment; + } + } + + return _data.getDefaultSkin() != NULL ? _data.getDefaultSkin()->getAttachment(slotIndex, attachmentName) : NULL; + } + + void Skeleton::setAttachment(std::string slotName, std::string attachmentName) { + assert(slotName.length() > 0); + + for (int i = 0, n = static_cast(_slots.size()); i < n; ++i) { + Slot* slot = _slots[i]; + if (slot->_data.getName() == slotName) { + Attachment* attachment = NULL; + if (attachmentName.length() > 0) { + attachment = getAttachment(i, attachmentName); + + assert(attachment != NULL); + } + + slot->setAttachment(attachment); + + return; + } + } + + printf("Slot not found: %s", slotName.c_str()); + + assert(false); + } + + IkConstraint* Skeleton::findIkConstraint(std::string constraintName) { + assert(constraintName.length() > 0); + + for (int i = 0, n = static_cast(_ikConstraints.size()); i < n; ++i) { + IkConstraint* ikConstraint = _ikConstraints[i]; + if (ikConstraint->_data.getName() == constraintName) { + return ikConstraint; + } + } + return NULL; + } + + TransformConstraint* Skeleton::findTransformConstraint(std::string constraintName) { + assert(constraintName.length() > 0); + + for (int i = 0, n = static_cast(_transformConstraints.size()); i < n; ++i) { + TransformConstraint* transformConstraint = _transformConstraints[i]; + if (transformConstraint->_data.getName() == constraintName) { + return transformConstraint; + } + } + + return NULL; + } + + PathConstraint* Skeleton::findPathConstraint(std::string constraintName) { + assert(constraintName.length() > 0); + + for (int i = 0, n = static_cast(_pathConstraints.size()); i < n; ++i) { + PathConstraint* constraint = _pathConstraints[i]; + if (constraint->_data.getName() == constraintName) { + return constraint; + } + } + + return NULL; + } + + void Skeleton::update(float delta) { + _time += delta; + } + + void Skeleton::getBounds(float& outX, float& outY, float& outWidth, float& outHeight, Vector& outVertexBuffer) { + float minX = std::numeric_limits::max(); + float minY = std::numeric_limits::max(); + float maxX = std::numeric_limits::min(); + float maxY = std::numeric_limits::min(); + + for (Slot** i = _drawOrder.begin(); i != _drawOrder.end(); ++i) { + Slot* slot = (*i); + int verticesLength = 0; + Attachment* attachment = slot->getAttachment(); + + if (attachment != NULL && attachment->getRTTI().derivesFrom(RegionAttachment::rtti)) { + RegionAttachment* regionAttachment = static_cast(attachment); + + verticesLength = 8; + if (outVertexBuffer.size() < 8) { + outVertexBuffer.reserve(8); + outVertexBuffer.setSize(8); + } + regionAttachment->computeWorldVertices(slot->getBone(), outVertexBuffer, 0); + } + else if (attachment != NULL && attachment->getRTTI().derivesFrom(MeshAttachment::rtti)) { + MeshAttachment* mesh = static_cast(attachment); + + verticesLength = mesh->getWorldVerticesLength(); + if (outVertexBuffer.size() < verticesLength) { + outVertexBuffer.reserve(verticesLength); + outVertexBuffer.setSize(verticesLength); + } + + mesh->computeWorldVertices(*slot, 0, verticesLength, outVertexBuffer, 0); + } + + for (int ii = 0; ii < verticesLength; ii += 2) { + float vx = outVertexBuffer[ii]; + float vy = outVertexBuffer[ii + 1]; + + minX = MIN(minX, vx); + minY = MIN(minY, vy); + maxX = MAX(maxX, vx); + maxY = MAX(maxY, vy); + } + } + + outX = minX; + outY = minY; + outWidth = maxX - minX; + outHeight = maxY - minY; + } + + Bone* Skeleton::getRootBone() { + return _bones.size() == 0 ? NULL : _bones[0]; + } + + const SkeletonData& Skeleton::getData() { + return _data; + } + + Vector& Skeleton::getBones() { + return _bones; + } + + Vector& Skeleton::getUpdateCacheList() { + return _updateCache; + } + + Vector& Skeleton::getSlots() { + return _slots; + } + + Vector& Skeleton::getDrawOrder() { + return _drawOrder; + } + + Vector& Skeleton::getIkConstraints() { + return _ikConstraints; + } + + Vector& Skeleton::getPathConstraints() { + return _pathConstraints; + } + + Vector& Skeleton::getTransformConstraints() { + return _transformConstraints; + } + + Skin* Skeleton::getSkin() { + return _skin; + } + + float Skeleton::getR() { + return _r; + } + + void Skeleton::setR(float inValue) { + _r = inValue; + } + + float Skeleton::getG() { + return _g; + } + + void Skeleton::setG(float inValue) { + _g = inValue; + } + + float Skeleton::getB() { + return _b; + } + + void Skeleton::setB(float inValue) { + _b = inValue; + } + + float Skeleton::getA() { + return _a; + } + + void Skeleton::setA(float inValue) { + _a = inValue; + } + + float Skeleton::getTime() { + return _time; + } + + void Skeleton::setTime(float inValue) { + _time = inValue; + } + + float Skeleton::getX() { + return _x; + } + + void Skeleton::setX(float inValue) { + _x = inValue; + } + + float Skeleton::getY() { + return _y; + } + + void Skeleton::setY(float inValue) { + _y = inValue; + } + + bool Skeleton::getFlipX() { + return _flipX; + } + + void Skeleton::setFlipX(float inValue) { + _flipX = inValue; + } + + bool Skeleton::getFlipY() { + return _flipY; + } + + void Skeleton::setFlipY(float inValue) { + _flipY = inValue; + } + + void Skeleton::sortIkConstraint(IkConstraint* constraint) { + Bone* target = constraint->getTarget(); + sortBone(target); + + Vector& constrained = constraint->getBones(); + Bone* parent = constrained[0]; + sortBone(parent); + + if (constrained.size() > 1) { + Bone* child = constrained[constrained.size() - 1]; + if (!_updateCache.contains(child)) { + _updateCacheReset.push_back(child); + } + } + + _updateCache.push_back(constraint); + + sortReset(parent->getChildren()); + constrained[constrained.size() - 1]->_sorted = true; + } + + void Skeleton::sortPathConstraint(PathConstraint* constraint) { + Slot* slot = constraint->getTarget(); + int slotIndex = slot->_data.getIndex(); + Bone& slotBone = slot->_bone; + + if (_skin != NULL) { + sortPathConstraintAttachment(_skin, slotIndex, slotBone); + } + + if (_data._defaultSkin != NULL && _data._defaultSkin != _skin) { + sortPathConstraintAttachment(_data._defaultSkin, slotIndex, slotBone); + } + + for (int ii = 0, nn = static_cast(_data._skins.size()); ii < nn; ++ii) { + sortPathConstraintAttachment(_data._skins[ii], slotIndex, slotBone); + } + + Attachment* attachment = slot->_attachment; + if (attachment != NULL && attachment->getRTTI().derivesFrom(PathAttachment::rtti)) { + sortPathConstraintAttachment(attachment, slotBone); + } + + Vector& constrained = constraint->getBones(); + int boneCount = static_cast(constrained.size()); + for (int i = 0; i < boneCount; ++i) { + sortBone(constrained[i]); + } + + _updateCache.push_back(constraint); + + for (int i = 0; i < boneCount; ++i) { + sortReset(constrained[i]->getChildren()); + } + + for (int i = 0; i < boneCount; ++i) { + constrained[i]->_sorted = true; + } + } + + void Skeleton::sortTransformConstraint(TransformConstraint* constraint) { + sortBone(constraint->getTarget()); + + Vector& constrained = constraint->getBones(); + int boneCount = static_cast(constrained.size()); + if (constraint->_data.isLocal()) { + for (int i = 0; i < boneCount; ++i) { + Bone* child = constrained[i]; + sortBone(child->getParent()); + if (!_updateCache.contains(child)) { + _updateCacheReset.push_back(child); + } + } + } + else { + for (int i = 0; i < boneCount; ++i) { + sortBone(constrained[i]); + } + } + + _updateCache.push_back(constraint); + + for (int i = 0; i < boneCount; ++i) { + sortReset(constrained[i]->getChildren()); + } + + for (int i = 0; i < boneCount; ++i) { + constrained[i]->_sorted = true; + } + } + + void Skeleton::sortPathConstraintAttachment(Skin* skin, int slotIndex, Bone& slotBone) { + HashMap& attachments = skin->getAttachments(); + + for (typename HashMap::Iterator i = attachments.begin(); i != attachments.end(); ++i) { + Skin::AttachmentKey key = i.first(); + if (key._slotIndex == slotIndex) { + Attachment* value = i.second(); + sortPathConstraintAttachment(value, slotBone); + } + } + } + + void Skeleton::sortPathConstraintAttachment(Attachment* attachment, Bone& slotBone) { + if (attachment == NULL || attachment->getRTTI().derivesFrom(PathAttachment::rtti)) { + return; + } + + PathAttachment* pathAttachment = static_cast(attachment); + Vector& pathBonesRef = pathAttachment->getBones(); + Vector pathBones = pathBonesRef; + if (pathBones.size() == 0) { + sortBone(&slotBone); + } + else { + for (int i = 0, n = static_cast(pathBones.size()); i < n;) { + int nn = pathBones[i++]; + nn += i; + while (i < nn) { + sortBone(_bones[pathBones[i++]]); + } + } + } + } + + void Skeleton::sortBone(Bone* bone) { + assert(bone != NULL); + + if (bone->_sorted) { + return; + } + + Bone* parent = bone->_parent; + if (parent != NULL) { + sortBone(parent); + } + + bone->_sorted = true; + + _updateCache.push_back(bone); + } + + void Skeleton::sortReset(Vector& bones) { + for (Bone** i = bones.begin(); i != bones.end(); ++i) { + Bone* bone = (*i); + if (bone->_sorted) { + sortReset(bone->getChildren()); + } + + bone->_sorted = false; + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp new file mode 100644 index 000000000..364f128b0 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp @@ -0,0 +1,1207 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Spine { + const int SkeletonBinary::BONE_ROTATE = 0; + const int SkeletonBinary::BONE_TRANSLATE = 1; + const int SkeletonBinary::BONE_SCALE = 2; + const int SkeletonBinary::BONE_SHEAR = 3; + + const int SkeletonBinary::SLOT_ATTACHMENT = 0; + const int SkeletonBinary::SLOT_COLOR = 1; + const int SkeletonBinary::SLOT_TWO_COLOR = 2; + + const int SkeletonBinary::PATH_POSITION = 0; + const int SkeletonBinary::PATH_SPACING = 1; + const int SkeletonBinary::PATH_MIX = 2; + + const int SkeletonBinary::CURVE_LINEAR = 0; + const int SkeletonBinary::CURVE_STEPPED = 1; + const int SkeletonBinary::CURVE_BEZIER = 2; + + const TransformMode SkeletonBinary::TRANSFORM_MODE_VALUES[5] = { + TransformMode_Normal, + TransformMode_OnlyTranslation, + TransformMode_NoRotationOrReflection, + TransformMode_NoScale, + TransformMode_NoScaleOrReflection + }; + + SkeletonBinary::SkeletonBinary(Vector& atlasArray) : _attachmentLoader(NEW(AtlasAttachmentLoader)), _error(), _scale(1), _ownsLoader(true) { + new (_attachmentLoader) AtlasAttachmentLoader(atlasArray); + } + + SkeletonBinary::SkeletonBinary(AttachmentLoader* attachmentLoader) : _attachmentLoader(attachmentLoader), _error(), _scale(1), _ownsLoader(false) { + assert(_attachmentLoader != NULL); + } + + SkeletonBinary::~SkeletonBinary() { + ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); + + if (_ownsLoader) { + DESTROY(AttachmentLoader, _attachmentLoader); + } + } + + SkeletonData* SkeletonBinary::readSkeletonData(const unsigned char* binary, const int length) { + int i, ii, nonessential; + SkeletonData* skeletonData; + + DataInput* input = CALLOC(DataInput, 1); + input->cursor = binary; + input->end = binary + length; + + _linkedMeshes.clear(); + + skeletonData = NEW(SkeletonData); + new (skeletonData) SkeletonData(); + + char* skeletonData_hash = readString(input); + skeletonData->_hash = std::string(skeletonData_hash); + FREE(skeletonData_hash); + + char* skeletonData_version = readString(input); + skeletonData->_version = std::string(skeletonData_version); + FREE(skeletonData_version); + + skeletonData->_width = readFloat(input); + skeletonData->_height = readFloat(input); + + nonessential = readBoolean(input); + + if (nonessential) { + /* Skip images path & fps */ + readFloat(input); + FREE(readString(input)); + } + + /* Bones. */ + int bonesCount = readVarint(input, 1); + skeletonData->_bones.reserve(bonesCount); + skeletonData->_bones.setSize(bonesCount); + for (i = 0; i < bonesCount; ++i) { + BoneData* data; + int mode; + const char* name = readString(input); + BoneData* parent = i == 0 ? 0 : skeletonData->_bones[readVarint(input, 1)]; + + data = NEW(BoneData); + new (data) BoneData(i, std::string(name), parent); + + FREE(name); + + data->_rotation = readFloat(input); + data->_x = readFloat(input) * _scale; + data->_y = readFloat(input) * _scale; + data->_scaleX = readFloat(input); + data->_scaleY = readFloat(input); + data->_shearX = readFloat(input); + data->_shearY = readFloat(input); + data->_length = readFloat(input) * _scale; + + mode = readVarint(input, 1); + switch (mode) { + case 0: + data->_transformMode = TransformMode_Normal; + break; + case 1: + data->_transformMode = TransformMode_OnlyTranslation; + break; + case 2: + data->_transformMode = TransformMode_NoRotationOrReflection; + break; + case 3: + data->_transformMode = TransformMode_NoScale; + break; + case 4: + data->_transformMode = TransformMode_NoScaleOrReflection; + break; + } + + if (nonessential) { + /* Skip bone color. */ + readInt(input); + } + + skeletonData->_bones[i] = data; + } + + /* Slots. */ + int slotsCount = readVarint(input, 1); + skeletonData->_slots.reserve(slotsCount); + skeletonData->_slots.setSize(slotsCount); + for (i = 0; i < slotsCount; ++i) { + int r, g, b, a; + const char* slotName = readString(input); + BoneData* boneData = skeletonData->_bones[readVarint(input, 1)]; + + SlotData* slotData = NEW(SlotData); + new (slotData) SlotData(i, std::string(slotName), *boneData); + + FREE(slotName); + readColor(input, &slotData->_r, &slotData->_g, &slotData->_b, &slotData->_a); + r = readByte(input); + g = readByte(input); + b = readByte(input); + a = readByte(input); + if (!(r == 0xff && g == 0xff && b == 0xff && a == 0xff)) { + slotData->_r2 = r / 255.0f; + slotData->_g2 = g / 255.0f; + slotData->_b2 = b / 255.0f; + } + char* slotData_attachmentName = readString(input); + slotData->_attachmentName = std::string(slotData_attachmentName); + FREE(slotData_attachmentName); + slotData->_blendMode = static_cast(readVarint(input, 1)); + + skeletonData->_slots[i] = slotData; + } + + /* IK constraints. */ + int ikConstraintsCount = readVarint(input, 1); + skeletonData->_ikConstraints.reserve(ikConstraintsCount); + skeletonData->_ikConstraints.setSize(ikConstraintsCount); + for (i = 0; i < ikConstraintsCount; ++i) { + const char* name = readString(input); + + IkConstraintData* data = NEW(IkConstraintData); + new (data) IkConstraintData(std::string(name)); + + data->_order = readVarint(input, 1); + + FREE(name); + int bonesCount = readVarint(input, 1); + data->_bones.reserve(bonesCount); + data->_bones.setSize(bonesCount); + for (ii = 0; ii < bonesCount; ++ii) { + data->_bones[ii] = skeletonData->_bones[readVarint(input, 1)]; + } + data->_target = skeletonData->_bones[readVarint(input, 1)]; + data->_mix = readFloat(input); + data->_bendDirection = readSByte(input); + + skeletonData->_ikConstraints[i] = data; + } + + /* Transform constraints. */ + int transformConstraintsCount = readVarint(input, 1); + skeletonData->_transformConstraints.reserve(transformConstraintsCount); + skeletonData->_transformConstraints.setSize(transformConstraintsCount); + for (i = 0; i < transformConstraintsCount; ++i) { + const char* name = readString(input); + + TransformConstraintData* data = NEW(TransformConstraintData); + new (data) TransformConstraintData(std::string(name)); + + data->_order = readVarint(input, 1); + FREE(name); + int bonesCount = readVarint(input, 1); + data->_bones.reserve(bonesCount); + data->_bones.setSize(bonesCount); + for (ii = 0; ii < bonesCount; ++ii) { + data->_bones[ii] = skeletonData->_bones[readVarint(input, 1)]; + } + data->_target = skeletonData->_bones[readVarint(input, 1)]; + data->_local = readBoolean(input); + data->_relative = readBoolean(input); + data->_offsetRotation = readFloat(input); + data->_offsetX = readFloat(input) * _scale; + data->_offsetY = readFloat(input) * _scale; + data->_offsetScaleX = readFloat(input); + data->_offsetScaleY = readFloat(input); + data->_offsetShearY = readFloat(input); + data->_rotateMix = readFloat(input); + data->_translateMix = readFloat(input); + data->_scaleMix = readFloat(input); + data->_shearMix = readFloat(input); + + skeletonData->_transformConstraints[i] = data; + } + + /* Path constraints */ + int pathConstraintsCount = readVarint(input, 1); + skeletonData->_pathConstraints.reserve(pathConstraintsCount); + skeletonData->_pathConstraints.setSize(pathConstraintsCount); + for (i = 0; i < pathConstraintsCount; ++i) { + const char* name = readString(input); + + PathConstraintData* data = NEW(PathConstraintData); + new (data) PathConstraintData(std::string(name)); + + data->_order = readVarint(input, 1); + FREE(name); + + int bonesCount = readVarint(input, 1); + data->_bones.reserve(bonesCount); + data->_bones.setSize(bonesCount); + for (ii = 0; ii < bonesCount; ++ii) { + data->_bones[ii] = skeletonData->_bones[readVarint(input, 1)]; + } + data->_target = skeletonData->_slots[readVarint(input, 1)]; + data->_positionMode = static_cast(readVarint(input, 1)); + data->_spacingMode = static_cast(readVarint(input, 1)); + data->_rotateMode = static_cast(readVarint(input, 1)); + data->_offsetRotation = readFloat(input); + data->_position = readFloat(input); + if (data->_positionMode == PositionMode_Fixed) { + data->_position *= _scale; + } + + data->_spacing = readFloat(input); + if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) { + data->_spacing *= _scale; + } + data->_rotateMix = readFloat(input); + data->_translateMix = readFloat(input); + + skeletonData->_pathConstraints[i] = data; + } + + /* Default skin. */ + skeletonData->_defaultSkin = readSkin(input, "default", skeletonData, nonessential); + int skinsCount = readVarint(input, 1); + + if (skeletonData->_defaultSkin) { + ++skinsCount; + } + + skeletonData->_skins.reserve(skinsCount); + skeletonData->_skins.setSize(skinsCount); + + if (skeletonData->_defaultSkin) { + skeletonData->_skins[0] = skeletonData->_defaultSkin; + } + + /* Skins. */ + for (i = skeletonData->_defaultSkin ? 1 : 0; i < skeletonData->_skins.size(); ++i) { + const char* skinName = readString(input); + skeletonData->_skins[i] = readSkin(input, skinName, skeletonData, nonessential); + FREE(skinName); + } + + /* Linked meshes. */ + for (int i = 0, n = static_cast(_linkedMeshes.size()); i < n; ++i) { + LinkedMesh* linkedMesh = _linkedMeshes[i]; + Skin* skin = linkedMesh->_skin.length() == 0 ? skeletonData->getDefaultSkin() : skeletonData->findSkin(linkedMesh->_skin); + if (skin == NULL) { + FREE(input); + DESTROY(SkeletonData, skeletonData); + setError("Skin not found: ", linkedMesh->_skin.c_str()); + return NULL; + } + Attachment* parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent); + if (parent == NULL) { + FREE(input); + DESTROY(SkeletonData, skeletonData); + setError("Parent mesh not found: ", linkedMesh->_parent.c_str()); + return NULL; + } + linkedMesh->_mesh->_parentMesh = static_cast(parent); + linkedMesh->_mesh->updateUVs(); + } + _linkedMeshes.clear(); + + /* Events. */ + int eventsCount = readVarint(input, 1); + skeletonData->_events.reserve(eventsCount); + skeletonData->_events.setSize(eventsCount); + for (i = 0; i < eventsCount; ++i) { + const char* name = readString(input); + EventData* eventData = NEW(EventData); + new (eventData) EventData(std::string(name)); + FREE(name); + eventData->_intValue = readVarint(input, 0); + eventData->_floatValue = readFloat(input); + const char* eventData_stringValue = readString(input); + eventData->_stringValue = std::string(eventData_stringValue); + FREE(eventData_stringValue); + skeletonData->_events[i] = eventData; + } + + /* Animations. */ + int animationsCount = readVarint(input, 1); + skeletonData->_animations.reserve(animationsCount); + skeletonData->_animations.setSize(animationsCount); + for (i = 0; i < animationsCount; ++i) { + const char* name = readString(input); + Animation* animation = readAnimation(name, input, skeletonData); + FREE(name); + if (!animation) { + FREE(input); + DESTROY(SkeletonData, skeletonData); + return NULL; + } + skeletonData->_animations[i] = animation; + } + + FREE(input); + + return skeletonData; + } + + SkeletonData* SkeletonBinary::readSkeletonDataFile(const char* path) { + int length; + SkeletonData* skeletonData; + const char* binary = SPINE_EXTENSION->spineReadFile(path, &length); + if (length == 0 || !binary) { + setError("Unable to read skeleton file: ", path); + return NULL; + } + skeletonData = readSkeletonData((unsigned char*)binary, length); + FREE(binary); + return skeletonData; + } + + void SkeletonBinary::setError(const char* value1, const char* value2) { + char message[256]; + int length; + strcpy(message, value1); + length = (int)strlen(value1); + if (value2) { + strncat(message + length, value2, 255 - length); + } + + _error = std::string(message); + } + + char* SkeletonBinary::readString(DataInput* input) { + int length = readVarint(input, 1); + char* string; + if (length == 0) { + return NULL; + } + string = MALLOC(char, length); + memcpy(string, input->cursor, length - 1); + input->cursor += length - 1; + string[length - 1] = '\0'; + return string; + } + + float SkeletonBinary::readFloat(DataInput* input) { + union { + int intValue; + float floatValue; + } intToFloat; + + intToFloat.intValue = readInt(input); + + return intToFloat.floatValue; + } + + unsigned char SkeletonBinary::readByte(DataInput* input) { + return *input->cursor++; + } + + signed char SkeletonBinary::readSByte(DataInput* input) { + return (signed char)readByte(input); + } + + int SkeletonBinary::readBoolean(DataInput* input) { + return readByte(input) != 0; + } + + int SkeletonBinary::readInt(DataInput* input) { + int result = readByte(input); + result <<= 8; + result |= readByte(input); + result <<= 8; + result |= readByte(input); + result <<= 8; + result |= readByte(input); + return result; + } + + void SkeletonBinary::readColor(DataInput* input, float *r, float *g, float *b, float *a) { + *r = readByte(input) / 255.0f; + *g = readByte(input) / 255.0f; + *b = readByte(input) / 255.0f; + *a = readByte(input) / 255.0f; + } + + int SkeletonBinary::readVarint(DataInput* input, bool optimizePositive) { + unsigned char b = readByte(input); + int value = b & 0x7F; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 7; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 14; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 21; + if (b & 0x80) value |= (readByte(input) & 0x7F) << 28; + } + } + } + + if (!optimizePositive) { + value = (((unsigned int)value >> 1) ^ -(value & 1)); + } + + return value; + } + + Skin* SkeletonBinary::readSkin(DataInput* input, const char* skinName, SkeletonData* skeletonData, bool nonessential) { + Skin* skin = NULL; + int slotCount = readVarint(input, 1); + int i, ii, nn; + if (slotCount == 0) { + return NULL; + } + + skin = NEW(Skin); + new (skin) Skin(std::string(skinName)); + + for (i = 0; i < slotCount; ++i) { + int slotIndex = readVarint(input, 1); + for (ii = 0, nn = readVarint(input, 1); ii < nn; ++ii) { + const char* name = readString(input); + Attachment* attachment = readAttachment(input, skin, slotIndex, name, skeletonData, nonessential); + if (attachment) { + skin->addAttachment(slotIndex, std::string(name), attachment); + } + FREE(name); + } + } + + return skin; + } + + Attachment* SkeletonBinary::readAttachment(DataInput* input, Skin* skin, int slotIndex, const char* attachmentName, SkeletonData* skeletonData, bool nonessential) { + int i; + AttachmentType type; + const char* name = readString(input); + int freeName = name != 0; + if (!name) { + freeName = 0; + name = attachmentName; + } + + type = static_cast(readByte(input)); + + switch (type) { + case AttachmentType_Region: { + const char* path = readString(input); + RegionAttachment* region; + if (!path) { + path = name; + } + region = _attachmentLoader->newRegionAttachment(*skin, std::string(name), std::string(path)); + region->_path = std::string(path); + region->_rotation = readFloat(input); + region->_x = readFloat(input) * _scale; + region->_y = readFloat(input) * _scale; + region->_scaleX = readFloat(input); + region->_scaleY = readFloat(input); + region->_width = readFloat(input) * _scale; + region->_height = readFloat(input) * _scale; + readColor(input, ®ion->_r, ®ion->_g, ®ion->_b, ®ion->_a); + region->updateOffset(); + + if (freeName) { + FREE(name); + } + + return region; + } + case AttachmentType_Boundingbox: { + int vertexCount = readVarint(input, 1); + BoundingBoxAttachment* box = _attachmentLoader->newBoundingBoxAttachment(*skin, std::string(name)); + readVertices(input, static_cast(box), vertexCount); + if (nonessential) { + /* Skip color. */ + readInt(input); + } + if (freeName) { + FREE(name); + } + + return box; + } + case AttachmentType_Mesh: { + int vertexCount; + MeshAttachment* mesh; + const char* path = readString(input); + if (!path) { + path = name; + } + mesh = _attachmentLoader->newMeshAttachment(*skin, std::string(name), std::string(path)); + mesh->_path = std::string(path); + readColor(input, &mesh->_r, &mesh->_g, &mesh->_b, &mesh->_a); + vertexCount = readVarint(input, 1); + Vector float_array = readFloatArray(input, vertexCount << 1, 1); + mesh->setRegionUVs(float_array); + Vector triangles = readShortArray(input); + mesh->setTriangles(triangles); + readVertices(input, static_cast(mesh), vertexCount); + mesh->updateUVs(); + mesh->_hullLength = readVarint(input, 1) << 1; + if (nonessential) { + Vector edges = readShortArray(input); + mesh->setEdges(edges); + mesh->_width = readFloat(input) * _scale; + mesh->_height = readFloat(input) * _scale; + } + else { + mesh->_width = 0; + mesh->_height = 0; + } + + if (freeName) { + FREE(name); + } + + return mesh; + } + case AttachmentType_Linkedmesh: { + const char* skinName; + const char* parent; + MeshAttachment* mesh; + const char* path = readString(input); + if (!path) { + path = name; + } + + mesh = _attachmentLoader->newMeshAttachment(*skin, std::string(name), std::string(path)); + mesh->_path = path; + readColor(input, &mesh->_r, &mesh->_g, &mesh->_b, &mesh->_a); + skinName = readString(input); + parent = readString(input); + mesh->_inheritDeform = readBoolean(input); + if (nonessential) { + mesh->_width = readFloat(input) * _scale; + mesh->_height = readFloat(input) * _scale; + } + + LinkedMesh* linkedMesh = NEW(LinkedMesh); + new (linkedMesh) LinkedMesh(mesh, std::string(skinName), slotIndex, std::string(parent)); + _linkedMeshes.push_back(linkedMesh); + + if (freeName) { + FREE(name); + } + + FREE(skinName); + FREE(parent); + + return mesh; + } + case AttachmentType_Path: { + PathAttachment* path = _attachmentLoader->newPathAttachment(*skin, std::string(name)); + int vertexCount = 0; + path->_closed = readBoolean(input); + path->_constantSpeed = readBoolean(input); + vertexCount = readVarint(input, 1); + readVertices(input, static_cast(path), vertexCount); + int lengthsLength = vertexCount / 3; + path->_lengths.reserve(lengthsLength); + path->_lengths.setSize(lengthsLength); + for (i = 0; i < lengthsLength; ++i) { + path->_lengths[i] = readFloat(input) * _scale; + } + + if (nonessential) { + /* Skip color. */ + readInt(input); + } + + if (freeName) { + FREE(name); + } + + return path; + } + case AttachmentType_Point: { + PointAttachment* point = _attachmentLoader->newPointAttachment(*skin, std::string(name)); + point->_rotation = readFloat(input); + point->_x = readFloat(input) * _scale; + point->_y = readFloat(input) * _scale; + + if (nonessential) { + /* Skip color. */ + readInt(input); + } + + return point; + } + case AttachmentType_Clipping: { + int endSlotIndex = readVarint(input, 1); + int vertexCount = readVarint(input, 1); + ClippingAttachment* clip = _attachmentLoader->newClippingAttachment(*skin, name); + readVertices(input, static_cast(clip), vertexCount); + + if (nonessential) { + /* Skip color. */ + readInt(input); + } + + clip->_endSlot = skeletonData->_slots[endSlotIndex]; + + if (freeName) { + FREE(name); + } + + return clip; + } + } + + if (freeName) { + FREE(name); + } + + return NULL; + } + + void SkeletonBinary::readVertices(DataInput* input, VertexAttachment* attachment, int vertexCount) { + float scale = _scale; + int verticesLength = vertexCount << 1; + + if (!readBoolean(input)) { + attachment->setVertices(readFloatArray(input, verticesLength, scale)); + return; + } + + Vertices vertices; + vertices._bones.reserve(verticesLength * 3); + vertices._vertices.reserve(verticesLength * 3 * 3); + + for (int i = 0; i < vertexCount; ++i) { + int boneCount = readVarint(input, true); + vertices._bones.push_back(boneCount); + for (int ii = 0; ii < boneCount; ++ii) { + vertices._bones.push_back(readVarint(input, true)); + vertices._vertices.push_back(readFloat(input) * scale); + vertices._vertices.push_back(readFloat(input) * scale); + vertices._vertices.push_back(readFloat(input)); + } + } + + attachment->setVertices(vertices._vertices); + attachment->setBones(vertices._bones); + } + + Vector SkeletonBinary::readFloatArray(DataInput *input, int n, float scale) { + Vector array; + array.reserve(n); + array.setSize(n); + + int i; + if (scale == 1) { + for (i = 0; i < n; ++i) { + array[i] = readFloat(input); + } + } + else { + for (i = 0; i < n; ++i) { + array[i] = readFloat(input) * scale; + } + } + + return array; + } + + Vector SkeletonBinary::readShortArray(DataInput *input) { + int n = readVarint(input, 1); + + Vector array; + array.reserve(n); + array.setSize(n); + + int i; + for (i = 0; i < n; ++i) { + array[i] = readByte(input) << 8; + array[i] |= readByte(input); + } + + return array; + } + + Animation* SkeletonBinary::readAnimation(const char* name, DataInput* input, SkeletonData *skeletonData) { + Vector timelines; + float scale = _scale; + float duration = 0; + + // Slot timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int slotIndex = readVarint(input, true); + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + unsigned char timelineType = readByte(input); + int frameCount = readVarint(input, true); + switch (timelineType) { + case SLOT_ATTACHMENT: { + AttachmentTimeline* timeline = NEW(AttachmentTimeline); + new(timeline) AttachmentTimeline(frameCount); + timeline->_slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + const char* attachmentName = readString(input); + timeline->setFrame(frameIndex, readFloat(input), std::string(attachmentName)); + FREE(attachmentName); + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[frameCount - 1]); + break; + } + case SLOT_COLOR: { + ColorTimeline* timeline = NEW(ColorTimeline); + new(timeline) ColorTimeline(frameCount); + timeline->_slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + int color = readInt(input); + float r = ((color & 0xff000000) >> 24) / 255.0f; + float g = ((color & 0x00ff0000) >> 16) / 255.0f; + float b = ((color & 0x0000ff00) >> 8) / 255.0f; + float a = ((color & 0x000000ff)) / 255.0f; + timeline->setFrame(frameIndex, time, r, g, b, a); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[(frameCount - 1) * ColorTimeline::ENTRIES]); + break; + } + case SLOT_TWO_COLOR: { + TwoColorTimeline* timeline = NEW(TwoColorTimeline); + new(timeline) TwoColorTimeline(frameCount); + timeline->_slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + int color = readInt(input); + float r = ((color & 0xff000000) >> 24) / 255.0f; + float g = ((color & 0x00ff0000) >> 16) / 255.0f; + float b = ((color & 0x0000ff00) >> 8) / 255.0f; + float a = ((color & 0x000000ff)) / 255.0f; + int color2 = readInt(input); // 0x00rrggbb + float r2 = ((color2 & 0x00ff0000) >> 16) / 255.0f; + float g2 = ((color2 & 0x0000ff00) >> 8) / 255.0f; + float b2 = ((color2 & 0x000000ff)) / 255.0f; + + timeline->setFrame(frameIndex, time, r, g, b, a, r2, g2, b2); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[(frameCount - 1) * TwoColorTimeline::ENTRIES]); + break; + } + default: { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError("Invalid timeline type for a slot: ", skeletonData->_slots[slotIndex]->_name.c_str()); + return NULL; + } + } + } + } + + // Bone timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int boneIndex = readVarint(input, true); + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + unsigned char timelineType = readByte(input); + int frameCount = readVarint(input, true); + switch (timelineType) { + case BONE_ROTATE: { + RotateTimeline* timeline = NEW(RotateTimeline); + new(timeline) RotateTimeline(frameCount); + timeline->_boneIndex = boneIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + timeline->setFrame(frameIndex, readFloat(input), readFloat(input)); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[(frameCount - 1) * RotateTimeline::ENTRIES]); + break; + } + case BONE_TRANSLATE: + case BONE_SCALE: + case BONE_SHEAR: { + TranslateTimeline* timeline; + float timelineScale = 1; + if (timelineType == BONE_SCALE) { + timeline = NEW(ScaleTimeline); + new(timeline) ScaleTimeline(frameCount); + } + else if (timelineType == BONE_SHEAR) { + timeline = NEW(ShearTimeline); + new(timeline) ShearTimeline(frameCount); + } + else { + timeline = NEW(TranslateTimeline); + new(timeline) TranslateTimeline(frameCount); + timelineScale = scale; + } + timeline->_boneIndex = boneIndex; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + timeline->setFrame(frameIndex, readFloat(input), readFloat(input) * timelineScale, readFloat(input) * timelineScale); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[(frameCount - 1) * TranslateTimeline::ENTRIES]); + break; + } + default: { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError("Invalid timeline type for a bone: ", skeletonData->_bones[boneIndex]->_name.c_str()); + return NULL; + } + } + } + } + + // IK timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int index = readVarint(input, true); + int frameCount = readVarint(input, true); + IkConstraintTimeline* timeline = NEW(IkConstraintTimeline); + new(timeline) IkConstraintTimeline(frameCount); + timeline->_ikConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + timeline->setFrame(frameIndex, readFloat(input), readFloat(input), readSByte(input)); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[(frameCount - 1) * IkConstraintTimeline::ENTRIES]); + } + + // Transform constraint timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int index = readVarint(input, true); + int frameCount = readVarint(input, true); + TransformConstraintTimeline* timeline = NEW(TransformConstraintTimeline); + new(timeline) TransformConstraintTimeline(frameCount); + timeline->_transformConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + timeline->setFrame(frameIndex, readFloat(input), readFloat(input), readFloat(input), readFloat(input), readFloat(input)); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[(frameCount - 1) * TransformConstraintTimeline::ENTRIES]); + } + + // Path constraint timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + int index = readVarint(input, true); + PathConstraintData* data = skeletonData->_pathConstraints[index]; + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + int timelineType = readSByte(input); + int frameCount = readVarint(input, true); + switch(timelineType) { + case PATH_POSITION: + case PATH_SPACING: { + PathConstraintPositionTimeline* timeline; + float timelineScale = 1; + if (timelineType == PATH_SPACING) { + timeline = NEW(PathConstraintSpacingTimeline); + new(timeline) PathConstraintSpacingTimeline(frameCount); + + if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) { + timelineScale = scale; + } + } + else { + timeline = NEW(PathConstraintPositionTimeline); + new(timeline) PathConstraintPositionTimeline(frameCount); + + if (data->_positionMode == PositionMode_Fixed) { + timelineScale = scale; + } + } + timeline->_pathConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + timeline->setFrame(frameIndex, readFloat(input), readFloat(input) * timelineScale); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[(frameCount - 1) * PathConstraintPositionTimeline::ENTRIES]); + break; + } + case PATH_MIX: { + PathConstraintMixTimeline* timeline = NEW(PathConstraintMixTimeline); + new(timeline) PathConstraintMixTimeline(frameCount); + + timeline->_pathConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + timeline->setFrame(frameIndex, readFloat(input), readFloat(input), readFloat(input)); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[(frameCount - 1) * PathConstraintMixTimeline::ENTRIES]); + break; + } + } + } + } + + // Deform timelines. + for (int i = 0, n = readVarint(input, true); i < n; ++i) { + Skin* skin = skeletonData->_skins[readVarint(input, true)]; + for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) { + int slotIndex = readVarint(input, true); + for (int iii = 0, nnn = readVarint(input, true); iii < nnn; iii++) { + const char* attachmentName = readString(input); + Attachment* baseAttachment = skin->getAttachment(slotIndex, std::string(attachmentName)); + + if (!baseAttachment) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError("Attachment not found: ", attachmentName); + FREE(attachmentName); + return NULL; + } + + FREE(attachmentName); + + VertexAttachment* attachment = static_cast(baseAttachment); + + bool weighted = attachment->_bones.size() > 0; + Vector& vertices = attachment->_vertices; + int deformLength = weighted ? static_cast(vertices.size()) / 3 * 2 : static_cast(vertices.size()); + + int frameCount = readVarint(input, true); + + DeformTimeline* timeline = NEW(DeformTimeline); + new(timeline) DeformTimeline(frameCount); + + timeline->_slotIndex = slotIndex; + timeline->_attachment = attachment; + + for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { + float time = readFloat(input); + Vector deform; + int end = readVarint(input, true); + if (end == 0) { + if (weighted) { + deform.reserve(deformLength); + for (int i = 0; i < deformLength; ++i) { + deform.push_back(0); + } + } + else { + deform = vertices; + } + } + else { + deform.reserve(deformLength); + int start = readVarint(input, true); + end += start; + if (scale == 1) { + for (int v = start; v < end; ++v) { + deform[v] = readFloat(input); + } + } + else { + for (int v = start; v < end; ++v) { + deform[v] = readFloat(input) * scale; + } + } + + if (!weighted) { + for (int v = 0, vn = static_cast(deform.size()); v < vn; ++v) { + deform[v] += vertices[v]; + } + } + } + + timeline->setFrame(frameIndex, time, deform); + if (frameIndex < frameCount - 1) { + readCurve(input, frameIndex, timeline); + } + } + + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[frameCount - 1]); + } + } + } + + // Draw order timeline. + int drawOrderCount = readVarint(input, true); + if (drawOrderCount > 0) { + DrawOrderTimeline* timeline = NEW(DrawOrderTimeline); + new(timeline) DrawOrderTimeline(drawOrderCount); + + int slotCount = static_cast(skeletonData->_slots.size()); + for (int i = 0; i < drawOrderCount; ++i) { + float time = readFloat(input); + int offsetCount = readVarint(input, true); + + Vector drawOrder; + drawOrder.reserve(slotCount); + for (int ii = slotCount - 1; ii >= 0; --ii) { + drawOrder[ii] = -1; + } + + Vector unchanged; + unchanged.reserve(slotCount - offsetCount); + int originalIndex = 0, unchangedIndex = 0; + for (int ii = 0; ii < offsetCount; ++ii) { + int slotIndex = readVarint(input, true); + // Collect unchanged items. + while (originalIndex != slotIndex) { + unchanged[unchangedIndex++] = originalIndex++; + } + // Set changed items. + int index = originalIndex; + drawOrder[index + readVarint(input, true)] = originalIndex++; + } + + // Collect remaining unchanged items. + while (originalIndex < slotCount) { + unchanged[unchangedIndex++] = originalIndex++; + } + + // Fill in unchanged items. + for (int ii = slotCount - 1; ii >= 0; --ii) { + if (drawOrder[ii] == -1) { + drawOrder[ii] = unchanged[--unchangedIndex]; + } + } + timeline->setFrame(i, time, drawOrder); + } + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[drawOrderCount - 1]); + } + + // Event timeline. + int eventCount = readVarint(input, true); + if (eventCount > 0) { + EventTimeline* timeline = NEW(EventTimeline); + new(timeline) EventTimeline(eventCount); + + for (int i = 0; i < eventCount; ++i) { + float time = readFloat(input); + EventData* eventData = skeletonData->_events[readVarint(input, true)]; + Event* event = NEW(Event); + new(event) Event(time, *eventData); + + event->_intValue = readVarint(input, false); + event->_floatValue = readFloat(input); + bool freeString = readBoolean(input); + const char* event_stringValue = freeString ? readString(input) : eventData->_stringValue.c_str(); + event->_stringValue = std::string(event_stringValue); + if (freeString) { + FREE(event_stringValue); + } + timeline->setFrame(i, event); + } + + timelines.push_back(timeline); + duration = MAX(duration, timeline->_frames[eventCount - 1]); + } + + Animation* ret = NEW(Animation); + new (ret) Animation(std::string(name), timelines, duration); + + return ret; + } + + void SkeletonBinary::readCurve(DataInput* input, int frameIndex, CurveTimeline* timeline) { + switch (readByte(input)) { + case CURVE_STEPPED: { + timeline->setStepped(frameIndex); + break; + } + case CURVE_BEZIER: { + float cx1 = readFloat(input); + float cy1 = readFloat(input); + float cx2 = readFloat(input); + float cy2 = readFloat(input); + timeline->setCurve(frameIndex, cx1, cy1, cx2, cy2); + break; + } + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonBounds.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonBounds.cpp new file mode 100644 index 000000000..15ad8e4f4 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/SkeletonBounds.cpp @@ -0,0 +1,239 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include + +namespace Spine { + SkeletonBounds::SkeletonBounds() : _minX(0), _minY(0), _maxX(0), _maxY(0) { + // Empty + } + + void SkeletonBounds::update(Skeleton& skeleton, bool updateAabb) { + Vector& slots = skeleton._slots; + int slotCount = static_cast(slots.size()); + + _boundingBoxes.clear(); + for (int i = 0, n = static_cast(_polygons.size()); i < n; ++i) { + _polygonPool.push_back(_polygons[i]); + } + + _polygons.clear(); + + for (int i = 0; i < slotCount; i++) { + Slot* slot = slots[i]; + Attachment* attachment = slot->_attachment; + if (attachment == NULL || !attachment->getRTTI().derivesFrom(BoundingBoxAttachment::rtti)) { + continue; + } + BoundingBoxAttachment* boundingBox = static_cast(attachment); + _boundingBoxes.push_back(boundingBox); + + Polygon* polygonP = NULL; + int poolCount = static_cast(_polygonPool.size()); + if (poolCount > 0) { + polygonP = _polygonPool[poolCount - 1]; + _polygonPool.erase(poolCount - 1); + } + else { + Polygon* polygonP = NEW(Polygon); + new (polygonP) Polygon(); + } + + _polygons.push_back(polygonP); + + Polygon& polygon = *polygonP; + + int count = boundingBox->getWorldVerticesLength(); + polygon._count = count; + if (polygon._vertices.size() < count) { + polygon._vertices.reserve(count); + } + boundingBox->computeWorldVertices(*slot, polygon._vertices); + } + + if (updateAabb) { + aabbCompute(); + } + else { + _minX = std::numeric_limits::min(); + _minY = std::numeric_limits::min(); + _maxX = std::numeric_limits::max(); + _maxY = std::numeric_limits::max(); + } + } + + bool SkeletonBounds::aabbcontainsPoint(float x, float y) { + return x >= _minX && x <= _maxX && y >= _minY && y <= _maxY; + } + + bool SkeletonBounds::aabbintersectsSegment(float x1, float y1, float x2, float y2) { + float minX = _minX; + float minY = _minY; + float maxX = _maxX; + float maxY = _maxY; + + if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) { + return false; + } + + float m = (y2 - y1) / (x2 - x1); + float y = m * (minX - x1) + y1; + if (y > minY && y < maxY) { + return true; + } + y = m * (maxX - x1) + y1; + if (y > minY && y < maxY) { + return true; + } + float x = (minY - y1) / m + x1; + if (x > minX && x < maxX) { + return true; + } + x = (maxY - y1) / m + x1; + if (x > minX && x < maxX) { + return true; + } + return false; + } + + bool SkeletonBounds::aabbIntersectsSkeleton(SkeletonBounds bounds) { + return _minX < bounds._maxX && _maxX > bounds._minX && _minY < bounds._maxY && _maxY > bounds._minY; + } + + bool SkeletonBounds::containsPoint(Polygon* polygon, float x, float y) { + Vector& vertices = polygon->_vertices; + int nn = polygon->_count; + + int prevIndex = nn - 2; + bool inside = false; + for (int ii = 0; ii < nn; ii += 2) { + float vertexY = vertices[ii + 1]; + float prevY = vertices[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + float vertexX = vertices[ii]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) { + inside = !inside; + } + } + prevIndex = ii; + } + return inside; + } + + BoundingBoxAttachment* SkeletonBounds::containsPoint(float x, float y) { + for (int i = 0, n = static_cast(_polygons.size()); i < n; ++i) { + if (containsPoint(_polygons[i], x, y)) { + return _boundingBoxes[i]; + } + } + + return NULL; + } + + BoundingBoxAttachment* SkeletonBounds::intersectsSegment(float x1, float y1, float x2, float y2) { + for (int i = 0, n = static_cast(_polygons.size()); i < n; ++i) { + if (intersectsSegment(_polygons[i], x1, y1, x2, y2)) { + return _boundingBoxes[i]; + } + } + return NULL; + } + + bool SkeletonBounds::intersectsSegment(Polygon* polygon, float x1, float y1, float x2, float y2) { + Vector& vertices = polygon->_vertices; + int nn = polygon->_count; + + float width12 = x1 - x2, height12 = y1 - y2; + float det1 = x1 * y2 - y1 * x2; + float x3 = vertices[nn - 2], y3 = vertices[nn - 1]; + for (int ii = 0; ii < nn; ii += 2) { + float x4 = vertices[ii], y4 = vertices[ii + 1]; + float det2 = x3 * y4 - y3 * x4; + float width34 = x3 - x4, height34 = y3 - y4; + float det3 = width12 * height34 - height12 * width34; + float x = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { + float y = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) { + return true; + } + } + x3 = x4; + y3 = y4; + } + + return false; + } + + Polygon* SkeletonBounds::getPolygon(BoundingBoxAttachment* attachment) { + int index = _boundingBoxes.indexOf(attachment); + + return index == -1 ? NULL : _polygons[index]; + } + + float SkeletonBounds::getWidth() { + return _maxX - _minX; + } + + float SkeletonBounds::getHeight() { + return _maxY - _minY; + } + + void SkeletonBounds::aabbCompute() { + float minX = std::numeric_limits::min(); + float minY = std::numeric_limits::min(); + float maxX = std::numeric_limits::max(); + float maxY = std::numeric_limits::max(); + + for (int i = 0, n = static_cast(_polygons.size()); i < n; ++i) { + Polygon* polygon = _polygons[i]; + Vector& vertices = polygon->_vertices; + for (int ii = 0, nn = polygon->_count; ii < nn; ii += 2) { + float x = vertices[ii]; + float y = vertices[ii + 1]; + minX = MIN(minX, x); + minY = MIN(minY, y); + maxX = MAX(maxX, x); + maxY = MAX(maxY, y); + } + } + _minX = minX; + _minY = minY; + _maxX = maxX; + _maxY = maxY; + } +} + diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp new file mode 100644 index 000000000..7d5ae1ddd --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp @@ -0,0 +1,323 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +namespace Spine { + SkeletonClipping::SkeletonClipping() : _clipAttachment(NULL) { + _clipOutput.reserve(128); + _clippedVertices.reserve(128); + _clippedTriangles.reserve(128); + _clippedUVs.reserve(128); + } + + int SkeletonClipping::clipStart(Slot& slot, ClippingAttachment* clip) { + if (_clipAttachment != NULL) { + return 0; + } + + _clipAttachment = clip; + + 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)); + + _clippingPolygons = clippingPolygons; + + for (Vector** i = _clippingPolygons.begin(); i != _clippingPolygons.end(); ++i) { + Vector* polygonP = (*i); + Vector& polygon = *polygonP; + makeClockwise(polygon); + polygon.push_back(polygon[0]); + polygon.push_back(polygon[1]); + } + + return static_cast(_clippingPolygons.size()); + } + + void SkeletonClipping::clipEnd(Slot& slot) { + if (_clipAttachment != NULL && _clipAttachment->_endSlot == &slot._data) { + clipEnd(); + } + } + + void SkeletonClipping::clipEnd() { + if (_clipAttachment == NULL) { + return; + } + + _clipAttachment = NULL; + _clippingPolygons.clear(); + _clippedVertices.clear(); + _clippedTriangles.clear(); + _clippingPolygon.clear(); + } + + void SkeletonClipping::clipTriangles(Vector& vertices, int verticesLength, Vector& triangles, int trianglesLength, Vector& uvs) { + Vector& clipOutput = _clipOutput; + Vector& clippedVertices = _clippedVertices; + Vector& clippedTriangles = _clippedTriangles; + Vector< Vector* >& polygons = _clippingPolygons; + int polygonsCount = static_cast(_clippingPolygons.size()); + + int index = 0; + clippedVertices.clear(); + _clippedUVs.clear(); + clippedTriangles.clear(); + + for (int i = 0; i < trianglesLength; i += 3) { + int vertexOffset = triangles[i] << 1; + float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; + float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; + + vertexOffset = triangles[i + 1] << 1; + float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; + float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; + + vertexOffset = triangles[i + 2] << 1; + float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; + float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; + + for (int p = 0; p < polygonsCount; p++) { + int s = static_cast(clippedVertices.size()); + if (clip(x1, y1, x2, y2, x3, y3, *polygons[p], clipOutput)) { + int clipOutputLength = static_cast(clipOutput.size()); + if (clipOutputLength == 0) { + continue; + } + float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; + float d = 1 / (d0 * d2 + d1 * (y1 - y3)); + + 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]; + clippedVertices[s] = x; + clippedVertices[s + 1] = y; + float c0 = x - x3, c1 = y - y3; + float a = (d0 * c0 + d1 * c1) * d; + float b = (d4 * c0 + d2 * c1) * d; + float c = 1 - a - b; + _clippedUVs[s] = u1 * a + u2 * b + u3 * c; + _clippedUVs[s + 1] = v1 * a + v2 * b + v3 * c; + s += 2; + } + + 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++) { + clippedTriangles[s] = index; + clippedTriangles[s + 1] = index + ii; + clippedTriangles[s + 2] = index + ii + 1; + s += 3; + } + index += clipOutputCount + 1; + } + 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; + clippedVertices[s + 3] = y2; + clippedVertices[s + 4] = x3; + clippedVertices[s + 5] = y3; + + _clippedUVs[s] = u1; + _clippedUVs[s + 1] = v1; + _clippedUVs[s + 2] = u2; + _clippedUVs[s + 3] = v2; + _clippedUVs[s + 4] = u3; + _clippedUVs[s + 5] = v3; + + 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; + index += 3; + break; + } + } + } + } + + bool SkeletonClipping::isClipping() { + 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; + bool clipped = false; + + // Avoid copy at the end. + Vector input; + if (clippingArea.size() % 4 >= 2) { + input = output; + output = _scratch; + } + else { + input = _scratch; + } + + input.clear(); + input.push_back(x1); + input.push_back(y1); + input.push_back(x2); + input.push_back(y2); + input.push_back(x3); + input.push_back(y3); + input.push_back(x1); + input.push_back(y1); + output.clear(); + + Vector& clippingVertices = clippingArea; + int clippingVerticesLast = static_cast(clippingArea.size()) - 4; + for (int i = 0; ; i += 2) { + float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; + float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; + float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; + + Vector& inputVertices = input; + int inputVerticesLength = static_cast(input.size()) - 2, outputStart = static_cast(output.size()); + for (int ii = 0; ii < inputVerticesLength; ii += 2) { + float inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; + float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; + bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; + if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { + if (side2) { + // v1 inside, v2 inside + output.push_back(inputX2); + output.push_back(inputY2); + continue; + } + // v1 inside, v2 outside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + output.push_back(edgeX + (edgeX2 - edgeX) * ua); + output.push_back(edgeY + (edgeY2 - edgeY) * ua); + } + else if (side2) { + // v1 outside, v2 inside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + output.push_back(edgeX + (edgeX2 - edgeX) * ua); + output.push_back(edgeY + (edgeY2 - edgeY) * ua); + output.push_back(inputX2); + output.push_back(inputY2); + } + clipped = true; + } + + if (outputStart == output.size()) { + // All edges outside. + originalOutput.clear(); + return true; + } + + output.push_back(output[0]); + output.push_back(output[1]); + + if (i == clippingVerticesLast) { + break; + } + Vector temp = output; + output = input; + output.clear(); + input = temp; + } + + if (originalOutput != output) { + originalOutput.clear(); + for (int i = 0, n = static_cast(output.size()) - 2; i < n; ++i) { + originalOutput.push_back(output[i]); + } + } + else { + originalOutput.reserve(originalOutput.size() - 2); + originalOutput.setSize(originalOutput.size() - 2); + } + + return clipped; + } + + void SkeletonClipping::makeClockwise(Vector& polygon) { + int verticeslength = static_cast(polygon.size()); + + float area = polygon[verticeslength - 2] * polygon[1] - polygon[0] * polygon[verticeslength - 1], p1x, p1y, p2x, p2y; + + for (int i = 0, n = verticeslength - 3; i < n; i += 2) { + p1x = polygon[i]; + p1y = polygon[i + 1]; + p2x = polygon[i + 2]; + p2y = polygon[i + 3]; + area += p1x * p2y - p2x * p1y; + } + + if (area < 0) { + return; + } + + for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { + float x = polygon[i], y = polygon[i + 1]; + int other = lastX - i; + polygon[i] = polygon[other]; + polygon[i + 1] = polygon[other + 1]; + polygon[other] = x; + polygon[other + 1] = y; + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonData.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonData.cpp new file mode 100644 index 000000000..67159eac3 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/SkeletonData.cpp @@ -0,0 +1,236 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace Spine { + SkeletonData::SkeletonData() : + _name(), + _defaultSkin(NULL), + _width(0), + _height(0), + _version(), + _hash(), + _fps(0), + _imagesPath() { + // Empty + } + + SkeletonData::~SkeletonData() { + ContainerUtil::cleanUpVectorOfPointers(_bones); + ContainerUtil::cleanUpVectorOfPointers(_slots); + ContainerUtil::cleanUpVectorOfPointers(_skins); + + _defaultSkin = NULL; + + ContainerUtil::cleanUpVectorOfPointers(_events); + ContainerUtil::cleanUpVectorOfPointers(_animations); + ContainerUtil::cleanUpVectorOfPointers(_ikConstraints); + ContainerUtil::cleanUpVectorOfPointers(_transformConstraints); + ContainerUtil::cleanUpVectorOfPointers(_pathConstraints); + } + + BoneData* SkeletonData::findBone(std::string boneName) { + return ContainerUtil::findWithName(_bones, boneName); + } + + int SkeletonData::findBoneIndex(std::string boneName) { + return ContainerUtil::findIndexWithName(_bones, boneName); + } + + SlotData* SkeletonData::findSlot(std::string slotName) { + return ContainerUtil::findWithName(_slots, slotName); + } + + int SkeletonData::findSlotIndex(std::string slotName) { + return ContainerUtil::findIndexWithName(_slots, slotName); + } + + Skin* SkeletonData::findSkin(std::string skinName) { + return ContainerUtil::findWithName(_skins, skinName); + } + + EventData* SkeletonData::findEvent(std::string eventDataName) { + return ContainerUtil::findWithName(_events, eventDataName); + } + + Animation* SkeletonData::findAnimation(std::string animationName) { + return ContainerUtil::findWithName(_animations, animationName); + } + + IkConstraintData* SkeletonData::findIkConstraint(std::string constraintName) { + return ContainerUtil::findWithName(_ikConstraints, constraintName); + } + + TransformConstraintData* SkeletonData::findTransformConstraint(std::string constraintName) { + return ContainerUtil::findWithName(_transformConstraints, constraintName); + } + + PathConstraintData* SkeletonData::findPathConstraint(std::string constraintName) { + return ContainerUtil::findWithName(_pathConstraints, constraintName); + } + + int SkeletonData::findPathConstraintIndex(std::string pathConstraintName) { + return ContainerUtil::findIndexWithName(_pathConstraints, pathConstraintName); + } + + std::string SkeletonData::getName() { + return _name; + } + + void SkeletonData::setName(std::string inValue) { + _name = inValue; + } + + Vector& SkeletonData::getBones() { + return _bones; + } + + Vector& SkeletonData::getSlots() { + return _slots; + } + + Vector& SkeletonData::getSkins() { + return _skins; + } + + void SkeletonData::setSkins(Vector& inValue) { + _skins = inValue; + } + + Skin* SkeletonData::getDefaultSkin() { + return _defaultSkin; + } + + void SkeletonData::setDefaultSkin(Skin* inValue) { + _defaultSkin = inValue; + } + + Vector& SkeletonData::getEvents() { + return _events; + } + + void SkeletonData::setEvents(Vector& inValue) { + _events = inValue; + } + + Vector& SkeletonData::getAnimations() { + return _animations; + } + + void SkeletonData::setAnimations(Vector& inValue) { + _animations = inValue; + } + + Vector& SkeletonData::getIkConstraints() { + return _ikConstraints; + } + + void SkeletonData::setIkConstraints(Vector& inValue) { + _ikConstraints = inValue; + } + + Vector& SkeletonData::getTransformConstraints() { + return _transformConstraints; + } + + void SkeletonData::setTransformConstraints(Vector& inValue) { + _transformConstraints = inValue; + } + + Vector& SkeletonData::getPathConstraints() { + return _pathConstraints; + } + + void SkeletonData::setPathConstraints(Vector& inValue) { + _pathConstraints = inValue; + } + + float SkeletonData::getWidth() { + return _width; + } + + void SkeletonData::setWidth(float inValue) { + _width = inValue; + } + + float SkeletonData::getHeight() { + return _height; + } + + void SkeletonData::setHeight(float inValue) { + _height = inValue; + } + + std::string SkeletonData::getVersion() { + return _version; + } + + void SkeletonData::setVersion(std::string inValue) { + _version = inValue; + } + + std::string SkeletonData::getHash() { + return _hash; + } + + void SkeletonData::setHash(std::string inValue) { + _hash = inValue; + } + + std::string SkeletonData::getImagesPath() { + return _imagesPath; + } + + void SkeletonData::setImagesPath(std::string inValue) { + _imagesPath = inValue; + } + + float SkeletonData::getFps() { + return _fps; + } + + void SkeletonData::setFps(float inValue) { + _fps = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp new file mode 100644 index 000000000..3dbddb488 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp @@ -0,0 +1,1263 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) +#define strdup _strdup +#endif + +namespace Spine { + SkeletonJson::SkeletonJson(Vector& atlasArray) : _attachmentLoader(NEW(AtlasAttachmentLoader)), _scale(1), _ownsLoader(true) { + new (_attachmentLoader) AtlasAttachmentLoader(atlasArray); + } + + SkeletonJson::SkeletonJson(AttachmentLoader* attachmentLoader) : _attachmentLoader(attachmentLoader), _scale(1), _ownsLoader(false) { + assert(_attachmentLoader != NULL); + } + + SkeletonJson::~SkeletonJson() { + ContainerUtil::cleanUpVectorOfPointers(_linkedMeshes); + + if (_ownsLoader) { + DESTROY(AttachmentLoader, _attachmentLoader); + } + } + + SkeletonData* SkeletonJson::readSkeletonDataFile(const char* path) { + int length; + SkeletonData* skeletonData; + const char* json = SPINE_EXTENSION->spineReadFile(path, &length); + if (length == 0 || !json) { + setError(NULL, "Unable to read skeleton file: ", path); + return NULL; + } + + skeletonData = readSkeletonData(json); + + FREE(json); + + return skeletonData; + } + + SkeletonData* SkeletonJson::readSkeletonData(const char* json) { + int i, ii; + SkeletonData* skeletonData; + Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *slots, *skins, *animations, *events; + + _error.clear(); + _linkedMeshes.clear(); + + root = NEW(Json); + new (root) Json(json); + + if (!root) { + setError(NULL, "Invalid skeleton JSON: ", Json::getError()); + return NULL; + } + + skeletonData = NEW(SkeletonData); + new (skeletonData) SkeletonData(); + + skeleton = Json::getItem(root, "skeleton"); + if (skeleton) { + skeletonData->_hash = Json::getString(skeleton, "hash", 0); + skeletonData->_version = Json::getString(skeleton, "spine", 0); + skeletonData->_width = Json::getFloat(skeleton, "width", 0); + skeletonData->_height = Json::getFloat(skeleton, "height", 0); + } + + /* Bones. */ + bones = Json::getItem(root, "bones"); + skeletonData->_bones.reserve(bones->_size); + skeletonData->_bones.setSize(bones->_size); + int bonesCount = 0; + for (boneMap = bones->_child, i = 0; boneMap; boneMap = boneMap->_next, ++i) { + BoneData* data; + const char* transformMode; + + BoneData* parent = 0; + const char* parentName = Json::getString(boneMap, "parent", 0); + if (parentName) { + parent = skeletonData->findBone(parentName); + if (!parent) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Parent bone not found: ", parentName); + return NULL; + } + } + + data = NEW(BoneData); + new (data) BoneData(bonesCount, Json::getString(boneMap, "name", 0), parent); + + data->_length = Json::getFloat(boneMap, "length", 0) * _scale; + data->_x = Json::getFloat(boneMap, "x", 0) * _scale; + data->_y = Json::getFloat(boneMap, "y", 0) * _scale; + data->_rotation = Json::getFloat(boneMap, "rotation", 0); + data->_scaleX = Json::getFloat(boneMap, "scaleX", 1); + data->_scaleY = Json::getFloat(boneMap, "scaleY", 1); + data->_shearX = Json::getFloat(boneMap, "shearX", 0); + data->_shearY = Json::getFloat(boneMap, "shearY", 0); + transformMode = Json::getString(boneMap, "transform", "normal"); + data->_transformMode = TransformMode_Normal; + if (strcmp(transformMode, "normal") == 0) { + data->_transformMode = TransformMode_Normal; + } + if (strcmp(transformMode, "onlyTranslation") == 0) { + data->_transformMode = TransformMode_OnlyTranslation; + } + if (strcmp(transformMode, "noRotationOrReflection") == 0) { + data->_transformMode = TransformMode_NoRotationOrReflection; + } + if (strcmp(transformMode, "noScale") == 0) { + data->_transformMode = TransformMode_NoScale; + } + if (strcmp(transformMode, "noScaleOrReflection") == 0) { + data->_transformMode = TransformMode_NoScaleOrReflection; + } + + skeletonData->_bones[i] = data; + bonesCount++; + } + + /* Slots. */ + slots = Json::getItem(root, "slots"); + if (slots) { + Json *slotMap; + skeletonData->_slots.reserve(slots->_size); + skeletonData->_slots.setSize(slots->_size); + for (slotMap = slots->_child, i = 0; slotMap; slotMap = slotMap->_next, ++i) { + SlotData* data; + const char* color; + const char* dark; + Json *item; + + const char* boneName = Json::getString(slotMap, "bone", 0); + BoneData* boneData = skeletonData->findBone(boneName); + if (!boneData) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Slot bone not found: ", boneName); + return NULL; + } + + data = NEW(SlotData); + new (data) SlotData(i, Json::getString(slotMap, "name", 0), *boneData); + + color = Json::getString(slotMap, "color", 0); + if (color) { + data->_r = toColor(color, 0); + data->_g = toColor(color, 1); + data->_b = toColor(color, 2); + data->_a = toColor(color, 3); + } + + dark = Json::getString(slotMap, "dark", 0); + if (dark) { + data->_r2 = toColor(dark, 0); + data->_g2 = toColor(dark, 1); + data->_b2 = toColor(dark, 2); + data->_a2 = toColor(dark, 3); + data->_hasSecondColor = true; + } + + item = Json::getItem(slotMap, "attachment"); + if (item) { + data->setAttachmentName(item->_valueString); + } + + item = Json::getItem(slotMap, "blend"); + if (item) { + if (strcmp(item->_valueString, "additive") == 0) { + data->_blendMode = BlendMode_Additive; + } + else if (strcmp(item->_valueString, "multiply") == 0) { + data->_blendMode = BlendMode_Multiply; + } + else if (strcmp(item->_valueString, "screen") == 0) { + data->_blendMode = BlendMode_Screen; + } + } + + skeletonData->_slots[i] = data; + } + } + + /* IK constraints. */ + ik = Json::getItem(root, "ik"); + if (ik) { + Json *constraintMap; + skeletonData->_ikConstraints.reserve(ik->_size); + skeletonData->_ikConstraints.setSize(ik->_size); + for (constraintMap = ik->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { + const char* targetName; + + IkConstraintData* data = NEW(IkConstraintData); + new (data) IkConstraintData(Json::getString(constraintMap, "name", 0)); + + data->_order = Json::getInt(constraintMap, "order", 0); + + boneMap = Json::getItem(constraintMap, "bones"); + data->_bones.reserve(boneMap->_size); + data->_bones.setSize(boneMap->_size); + for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { + data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); + if (!data->_bones[ii]) { + DESTROY(SkeletonData, skeletonData); + setError(root, "IK bone not found: ", boneMap->_valueString); + return NULL; + } + } + + targetName = Json::getString(constraintMap, "target", 0); + data->_target = skeletonData->findBone(targetName); + if (!data->_target) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Target bone not found: ", targetName); + return NULL; + } + + data->_bendDirection = Json::getInt(constraintMap, "bendPositive", 1) ? 1 : -1; + data->_mix = Json::getFloat(constraintMap, "mix", 1); + + skeletonData->_ikConstraints[i] = data; + } + } + + /* Transform constraints. */ + transform = Json::getItem(root, "transform"); + if (transform) { + Json *constraintMap; + skeletonData->_transformConstraints.reserve(transform->_size); + skeletonData->_transformConstraints.setSize(transform->_size); + for (constraintMap = transform->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { + const char* name; + + TransformConstraintData* data = NEW(TransformConstraintData); + new (data) TransformConstraintData(Json::getString(constraintMap, "name", 0)); + + data->_order = Json::getInt(constraintMap, "order", 0); + + boneMap = Json::getItem(constraintMap, "bones"); + data->_bones.reserve(boneMap->_size); + data->_bones.setSize(boneMap->_size); + for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { + data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); + if (!data->_bones[ii]) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Transform bone not found: ", boneMap->_valueString); + return NULL; + } + } + + name = Json::getString(constraintMap, "target", 0); + data->_target = skeletonData->findBone(name); + if (!data->_target) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Target bone not found: ", name); + return NULL; + } + + data->_local = Json::getInt(constraintMap, "local", 0); + data->_relative = Json::getInt(constraintMap, "relative", 0); + data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0); + data->_offsetX = Json::getFloat(constraintMap, "x", 0) * _scale; + data->_offsetY = Json::getFloat(constraintMap, "y", 0) * _scale; + data->_offsetScaleX = Json::getFloat(constraintMap, "scaleX", 0); + data->_offsetScaleY = Json::getFloat(constraintMap, "scaleY", 0); + data->_offsetShearY = Json::getFloat(constraintMap, "shearY", 0); + + data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1); + data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1); + data->_scaleMix = Json::getFloat(constraintMap, "scaleMix", 1); + data->_shearMix = Json::getFloat(constraintMap, "shearMix", 1); + + skeletonData->_transformConstraints[i] = data; + } + } + + /* Path constraints */ + path = Json::getItem(root, "path"); + if (path) { + Json *constraintMap; + skeletonData->_pathConstraints.reserve(path->_size); + skeletonData->_pathConstraints.setSize(path->_size); + for (constraintMap = path->_child, i = 0; constraintMap; constraintMap = constraintMap->_next, ++i) { + const char* name; + const char* item; + + PathConstraintData* data = NEW(PathConstraintData); + new (data) PathConstraintData(Json::getString(constraintMap, "name", 0)); + + data->_order = Json::getInt(constraintMap, "order", 0); + + boneMap = Json::getItem(constraintMap, "bones"); + data->_bones.reserve(boneMap->_size); + data->_bones.setSize(boneMap->_size); + for (boneMap = boneMap->_child, ii = 0; boneMap; boneMap = boneMap->_next, ++ii) { + data->_bones[ii] = skeletonData->findBone(boneMap->_valueString); + if (!data->_bones[ii]) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Path bone not found: ", boneMap->_valueString); + return NULL; + } + } + + name = Json::getString(constraintMap, "target", 0); + data->_target = skeletonData->findSlot(name); + if (!data->_target) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Target slot not found: ", name); + return NULL; + } + + item = Json::getString(constraintMap, "positionMode", "percent"); + if (strcmp(item, "fixed") == 0) { + data->_positionMode = PositionMode_Fixed; + } + else if (strcmp(item, "percent") == 0) { + data->_positionMode = PositionMode_Percent; + } + + item = Json::getString(constraintMap, "spacingMode", "length"); + if (strcmp(item, "length") == 0) { + data->_spacingMode = SpacingMode_Length; + } + else if (strcmp(item, "fixed") == 0) { + data->_spacingMode = SpacingMode_Fixed; + } + else if (strcmp(item, "percent") == 0) { + data->_spacingMode = SpacingMode_Percent; + } + + item = Json::getString(constraintMap, "rotateMode", "tangent"); + if (strcmp(item, "tangent") == 0) { + data->_rotateMode = RotateMode_Tangent; + } + else if (strcmp(item, "chain") == 0) { + data->_rotateMode = RotateMode_Chain; + } + else if (strcmp(item, "chainScale") == 0) { + data->_rotateMode = RotateMode_ChainScale; + } + + data->_offsetRotation = Json::getFloat(constraintMap, "rotation", 0); + data->_position = Json::getFloat(constraintMap, "position", 0); + if (data->_positionMode == PositionMode_Fixed) { + data->_position *= _scale; + } + data->_spacing = Json::getFloat(constraintMap, "spacing", 0); + if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) { + data->_spacing *= _scale; + } + data->_rotateMix = Json::getFloat(constraintMap, "rotateMix", 1); + data->_translateMix = Json::getFloat(constraintMap, "translateMix", 1); + + skeletonData->_pathConstraints[i] = data; + } + } + + /* Skins. */ + skins = Json::getItem(root, "skins"); + if (skins) { + Json *skinMap; + skeletonData->_skins.reserve(skins->_size); + skeletonData->_skins.setSize(skins->_size); + int skinsIndex = 0; + for (skinMap = skins->_child, i = 0; skinMap; skinMap = skinMap->_next, ++i) { + Json *attachmentsMap; + Json *curves; + + Skin* skin = NEW(Skin); + new (skin) Skin(skinMap->_name); + + skeletonData->_skins[skinsIndex++] = skin; + if (strcmp(skinMap->_name, "default") == 0) { + skeletonData->_defaultSkin = skin; + } + + for (attachmentsMap = skinMap->_child; attachmentsMap; attachmentsMap = attachmentsMap->_next) { + int slotIndex = skeletonData->findSlotIndex(attachmentsMap->_name); + Json *attachmentMap; + + for (attachmentMap = attachmentsMap->_child; attachmentMap; attachmentMap = attachmentMap->_next) { + Attachment* attachment; + const char* skinAttachmentName = attachmentMap->_name; + const char* attachmentName = Json::getString(attachmentMap, "name", skinAttachmentName); + const char* attachmentPath = Json::getString(attachmentMap, "path", attachmentName); + const char* color; + Json* entry; + + const char* typeString = Json::getString(attachmentMap, "type", "region"); + AttachmentType type; + if (strcmp(typeString, "region") == 0) { + type = AttachmentType_Region; + } + else if (strcmp(typeString, "mesh") == 0) { + type = AttachmentType_Mesh; + } + else if (strcmp(typeString, "linkedmesh") == 0) { + type = AttachmentType_Linkedmesh; + } + else if (strcmp(typeString, "boundingbox") == 0) { + type = AttachmentType_Boundingbox; + } + else if (strcmp(typeString, "path") == 0) { + type = AttachmentType_Path; + } + else if (strcmp(typeString, "clipping") == 0) { + type = AttachmentType_Clipping; + } + else { + DESTROY(SkeletonData, skeletonData); + setError(root, "Unknown attachment type: ", typeString); + return NULL; + } + + switch (type) { + case AttachmentType_Region: { + attachment = _attachmentLoader->newRegionAttachment(*skin, attachmentName, attachmentPath); + if (!attachment) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Error reading attachment: ", skinAttachmentName); + return NULL; + } + + RegionAttachment* region = static_cast(attachment); + region->_path = attachmentPath; + + region->_x = Json::getFloat(attachmentMap, "x", 0) * _scale; + region->_y = Json::getFloat(attachmentMap, "y", 0) * _scale; + region->_scaleX = Json::getFloat(attachmentMap, "scaleX", 1); + region->_scaleY = Json::getFloat(attachmentMap, "scaleY", 1); + region->_rotation = Json::getFloat(attachmentMap, "rotation", 0); + region->_width = Json::getFloat(attachmentMap, "width", 32) * _scale; + region->_height = Json::getFloat(attachmentMap, "height", 32) * _scale; + + color = Json::getString(attachmentMap, "color", 0); + if (color) { + region->_r = toColor(color, 0); + region->_g = toColor(color, 1); + region->_b = toColor(color, 2); + region->_a = toColor(color, 3); + } + + region->updateOffset(); + + break; + } + case AttachmentType_Mesh: + case AttachmentType_Linkedmesh: { + attachment = _attachmentLoader->newMeshAttachment(*skin, attachmentName, attachmentPath); + + MeshAttachment* mesh = static_cast(attachment); + mesh->_path = attachmentPath; + + color = Json::getString(attachmentMap, "color", 0); + if (color) { + mesh->_r = toColor(color, 0); + mesh->_g = toColor(color, 1); + mesh->_b = toColor(color, 2); + mesh->_a = toColor(color, 3); + } + + mesh->_width = Json::getFloat(attachmentMap, "width", 32) * _scale; + mesh->_height = Json::getFloat(attachmentMap, "height", 32) * _scale; + + entry = Json::getItem(attachmentMap, "parent"); + if (!entry) { + int verticesLength; + entry = Json::getItem(attachmentMap, "triangles"); + mesh->_triangles.reserve(entry->_size); + mesh->_triangles.setSize(entry->_size); + for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) { + mesh->_triangles[ii] = (unsigned short)entry->_valueInt; + } + + entry = Json::getItem(attachmentMap, "uvs"); + verticesLength = entry->_size; + mesh->_regionUVs.reserve(verticesLength); + mesh->_regionUVs.setSize(verticesLength); + for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) { + mesh->_regionUVs[ii] = entry->_valueFloat; + } + + readVertices(attachmentMap, mesh, verticesLength); + + mesh->updateUVs(); + + mesh->_hullLength = Json::getInt(attachmentMap, "hull", 0); + + entry = Json::getItem(attachmentMap, "edges"); + if (entry) { + mesh->_edges.reserve(entry->_size); + mesh->_edges.setSize(entry->_size); + for (entry = entry->_child, ii = 0; entry; entry = entry->_next, ++ii) { + mesh->_edges[ii] = entry->_valueInt; + } + } + } + else { + mesh->_inheritDeform = Json::getInt(attachmentMap, "deform", 1); + LinkedMesh* linkedMesh = NEW(LinkedMesh); + new (linkedMesh) LinkedMesh(mesh, std::string(Json::getString(attachmentMap, "skin", 0)), slotIndex, std::string(entry->_valueString)); + _linkedMeshes.push_back(linkedMesh); + } + break; + } + case AttachmentType_Boundingbox: { + attachment = _attachmentLoader->newBoundingBoxAttachment(*skin, attachmentName); + + BoundingBoxAttachment* box = static_cast(attachment); + + int vertexCount = Json::getInt(attachmentMap, "vertexCount", 0) << 1; + readVertices(attachmentMap, box, vertexCount); + break; + } + case AttachmentType_Path: { + attachment = _attachmentLoader->newPathAttachment(*skin, attachmentName); + + PathAttachment* pathAttatchment = static_cast(attachment); + + int vertexCount = 0; + pathAttatchment->_closed = Json::getInt(attachmentMap, "closed", 0); + pathAttatchment->_constantSpeed = Json::getInt(attachmentMap, "constantSpeed", 1); + vertexCount = Json::getInt(attachmentMap, "vertexCount", 0); + readVertices(attachmentMap, pathAttatchment, vertexCount << 1); + + pathAttatchment->_lengths.reserve(vertexCount / 3); + pathAttatchment->_lengths.setSize(vertexCount / 3); + + curves = Json::getItem(attachmentMap, "lengths"); + for (curves = curves->_child, ii = 0; curves; curves = curves->_next, ++ii) { + pathAttatchment->_lengths[ii] = curves->_valueFloat * _scale; + } + break; + } + case AttachmentType_Point: { + attachment = _attachmentLoader->newPointAttachment(*skin, attachmentName); + + PointAttachment* point = static_cast(attachment); + + point->_x = Json::getFloat(attachmentMap, "x", 0) * _scale; + point->_y = Json::getFloat(attachmentMap, "y", 0) * _scale; + point->_rotation = Json::getFloat(attachmentMap, "rotation", 0); + break; + } + case AttachmentType_Clipping: { + attachment = _attachmentLoader->newClippingAttachment(*skin, attachmentName); + + ClippingAttachment* clip = static_cast(attachment); + + int vertexCount = 0; + const char* end = Json::getString(attachmentMap, "end", 0); + if (end) { + SlotData* slot = skeletonData->findSlot(end); + clip->_endSlot = slot; + } + vertexCount = Json::getInt(attachmentMap, "vertexCount", 0) << 1; + readVertices(attachmentMap, clip, vertexCount); + break; + } + } + + skin->addAttachment(slotIndex, skinAttachmentName, attachment); + } + } + } + } + + /* Linked meshes. */ + for (int i = 0, n = static_cast(_linkedMeshes.size()); i < n; ++i) { + LinkedMesh* linkedMesh = _linkedMeshes[i]; + Skin* skin = linkedMesh->_skin.length() == 0 ? skeletonData->getDefaultSkin() : skeletonData->findSkin(linkedMesh->_skin); + if (skin == NULL) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Skin not found: ", linkedMesh->_skin.c_str()); + return NULL; + } + Attachment* parent = skin->getAttachment(linkedMesh->_slotIndex, linkedMesh->_parent); + if (parent == NULL) { + DESTROY(SkeletonData, skeletonData); + setError(root, "Parent mesh not found: ", linkedMesh->_parent.c_str()); + return NULL; + } + linkedMesh->_mesh->_parentMesh = static_cast(parent); + linkedMesh->_mesh->updateUVs(); + } + _linkedMeshes.clear(); + + /* Events. */ + events = Json::getItem(root, "events"); + if (events) { + Json *eventMap; + skeletonData->_events.reserve(events->_size); + skeletonData->_events.setSize(events->_size); + for (eventMap = events->_child, i = 0; eventMap; eventMap = eventMap->_next, ++i) { + EventData* eventData = NEW(EventData); + new (eventData) EventData(std::string(eventMap->_name)); + + eventData->_intValue = Json::getInt(eventMap, "int", 0); + eventData->_floatValue = Json::getFloat(eventMap, "float", 0); + const char* stringValue = Json::getString(eventMap, "string", 0); + eventData->_stringValue = std::string(stringValue ? stringValue : ""); + skeletonData->_events[i] = eventData; + } + } + + /* Animations. */ + animations = Json::getItem(root, "animations"); + if (animations) { + Json *animationMap; + skeletonData->_animations.reserve(animations->_size); + skeletonData->_animations.setSize(animations->_size); + int animationsIndex = 0; + for (animationMap = animations->_child; animationMap; animationMap = animationMap->_next) { + Animation* animation = readAnimation(animationMap, skeletonData); + if (!animation) { + DESTROY(SkeletonData, skeletonData); + DESTROY(Json, root); + return NULL; + } + skeletonData->_animations[animationsIndex++] = animation; + } + } + + DESTROY(Json, root); + + return skeletonData; + } + + float SkeletonJson::toColor(const char* value, int index) { + char digits[3]; + char *error; + int color; + + if (index >= strlen(value) / 2) { + return -1; + } + + value += index * 2; + + digits[0] = *value; + digits[1] = *(value + 1); + digits[2] = '\0'; + color = (int)strtoul(digits, &error, 16); + if (*error != 0) { + return -1; + } + + return color / (float)255; + } + + void SkeletonJson::readCurve(Json* frame, CurveTimeline* timeline, int frameIndex) { + Json* curve = Json::getItem(frame, "curve"); + if (!curve) { + return; + } + if (curve->_type == Json::JSON_STRING && strcmp(curve->_valueString, "stepped") == 0) { + timeline->setStepped(frameIndex); + } + else if (curve->_type == Json::JSON_ARRAY) { + Json* child0 = curve->_child; + Json* child1 = child0->_next; + Json* child2 = child1->_next; + Json* child3 = child2->_next; + timeline->setCurve(frameIndex, child0->_valueFloat, child1->_valueFloat, child2->_valueFloat, child3->_valueFloat); + } + } + + Animation* SkeletonJson::readAnimation(Json* root, SkeletonData *skeletonData) { + Vector timelines; + float duration = 0; + + int frameIndex; + Json* valueMap; + int timelinesCount = 0; + + Json* bones = Json::getItem(root, "bones"); + Json* slots = Json::getItem(root, "slots"); + Json* ik = Json::getItem(root, "ik"); + Json* transform = Json::getItem(root, "transform"); + Json* paths = Json::getItem(root, "paths"); + Json* deform = Json::getItem(root, "deform"); + Json* drawOrder = Json::getItem(root, "drawOrder"); + Json* events = Json::getItem(root, "events"); + Json *boneMap, *slotMap, *constraintMap; + if (!drawOrder) { + drawOrder = Json::getItem(root, "draworder"); + } + + for (boneMap = bones ? bones->_child : NULL; boneMap; boneMap = boneMap->_next) { + timelinesCount += boneMap->_size; + } + + for (slotMap = slots ? slots->_child : NULL; slotMap; slotMap = slotMap->_next) { + timelinesCount += slotMap->_size; + } + + timelinesCount += ik ? ik->_size : 0; + timelinesCount += transform ? transform->_size : 0; + + for (constraintMap = paths ? paths->_child : NULL; constraintMap; constraintMap = constraintMap->_next) { + timelinesCount += constraintMap->_size; + } + + for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next) { + for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next) { + timelinesCount += slotMap->_size; + } + } + + if (drawOrder) { + ++timelinesCount; + } + + if (events) { + ++timelinesCount; + } + + /** Slot timelines. */ + for (slotMap = slots ? slots->_child : 0; slotMap; slotMap = slotMap->_next) { + Json *timelineMap; + + int slotIndex = skeletonData->findSlotIndex(slotMap->_name); + if (slotIndex == -1) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Slot not found: ", slotMap->_name); + return NULL; + } + + for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) { + if (strcmp(timelineMap->_name, "attachment") == 0) { + AttachmentTimeline *timeline = NEW(AttachmentTimeline); + new (timeline) AttachmentTimeline(timelineMap->_size); + + timeline->_slotIndex = slotIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + Json* name = Json::getItem(valueMap, "name"); + std::string attachmentName = name->_type == Json::JSON_NULL ? std::string("") : std::string(name->_valueString); + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), attachmentName); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[timelineMap->_size - 1]); + + } + else if (strcmp(timelineMap->_name, "color") == 0) { + ColorTimeline *timeline = NEW(ColorTimeline); + new (timeline) ColorTimeline(timelineMap->_size); + + timeline->_slotIndex = slotIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + const char* s = Json::getString(valueMap, "color", 0); + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2), toColor(s, 3)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[(timelineMap->_size - 1) * ColorTimeline::ENTRIES]); + + } + else if (strcmp(timelineMap->_name, "twoColor") == 0) { + TwoColorTimeline *timeline = NEW(TwoColorTimeline); + new (timeline) TwoColorTimeline(timelineMap->_size); + + timeline->_slotIndex = slotIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + const char* s = Json::getString(valueMap, "light", 0); + const char* ds = Json::getString(valueMap, "dark", 0); + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2), + toColor(s, 3), toColor(ds, 0), toColor(ds, 1), toColor(ds, 2)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[(timelineMap->_size - 1) * TwoColorTimeline::ENTRIES]); + } + else { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Invalid timeline type for a slot: ", timelineMap->_name); + return NULL; + } + } + } + + /** Bone timelines. */ + for (boneMap = bones ? bones->_child : 0; boneMap; boneMap = boneMap->_next) { + Json *timelineMap; + + int boneIndex = skeletonData->findBoneIndex(boneMap->_name); + if (boneIndex == -1) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Bone not found: ", boneMap->_name); + return NULL; + } + + for (timelineMap = boneMap->_child; timelineMap; timelineMap = timelineMap->_next) { + if (strcmp(timelineMap->_name, "rotate") == 0) { + RotateTimeline *timeline = NEW(RotateTimeline); + new (timeline) RotateTimeline(timelineMap->_size); + + timeline->_boneIndex = boneIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "angle", 0)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[(timelineMap->_size - 1) * RotateTimeline::ENTRIES]); + } + else { + int isScale = strcmp(timelineMap->_name, "scale") == 0; + int isTranslate = strcmp(timelineMap->_name, "translate") == 0; + int isShear = strcmp(timelineMap->_name, "shear") == 0; + if (isScale || isTranslate || isShear) { + float timelineScale = isTranslate ? _scale: 1; + TranslateTimeline *timeline = 0; + if (isScale) { + timeline = NEW(ScaleTimeline); + new (timeline) ScaleTimeline(timelineMap->_size); + } + else if (isTranslate) { + timeline = NEW(TranslateTimeline); + new (timeline) TranslateTimeline(timelineMap->_size); + } + else if (isShear) { + timeline = NEW(ShearTimeline); + new (timeline) ShearTimeline(timelineMap->_size); + } + timeline->_boneIndex = boneIndex; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "x", 0) * timelineScale, Json::getFloat(valueMap, "y", 0) * timelineScale); + readCurve(valueMap, timeline, frameIndex); + } + + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[(timelineMap->_size - 1) * TranslateTimeline::ENTRIES]); + } + else { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Invalid timeline type for a bone: ", timelineMap->_name); + return NULL; + } + } + } + } + + /** IK constraint timelines. */ + for (constraintMap = ik ? ik->_child : 0; constraintMap; constraintMap = constraintMap->_next) { + IkConstraintData* constraint = skeletonData->findIkConstraint(constraintMap->_name); + IkConstraintTimeline *timeline = NEW(IkConstraintTimeline); + new (timeline) IkConstraintTimeline(constraintMap->_size); + + for (frameIndex = 0; frameIndex < static_cast(skeletonData->_ikConstraints.size()); ++frameIndex) { + if (constraint == skeletonData->_ikConstraints[frameIndex]) { + timeline->_ikConstraintIndex = frameIndex; + break; + } + } + for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "mix", 1), Json::getInt(valueMap, "bendPositive", 1) ? 1 : -1); + readCurve(valueMap, timeline, frameIndex); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[(constraintMap->_size - 1) * IkConstraintTimeline::ENTRIES]); + } + + /** Transform constraint timelines. */ + for (constraintMap = transform ? transform->_child : 0; constraintMap; constraintMap = constraintMap->_next) { + TransformConstraintData* constraint = skeletonData->findTransformConstraint(constraintMap->_name); + TransformConstraintTimeline *timeline = NEW(TransformConstraintTimeline); + new (timeline) TransformConstraintTimeline(constraintMap->_size); + + for (frameIndex = 0; frameIndex < skeletonData->_transformConstraints.size(); ++frameIndex) { + if (constraint == skeletonData->_transformConstraints[frameIndex]) { + timeline->_transformConstraintIndex = frameIndex; + break; + } + } + for (valueMap = constraintMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "rotateMix", 1), Json::getFloat(valueMap, "translateMix", 1), Json::getFloat(valueMap, "scaleMix", 1), Json::getFloat(valueMap, "shearMix", 1)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[(constraintMap->_size - 1) * TransformConstraintTimeline::ENTRIES]); + } + + /** Path constraint timelines. */ + for (constraintMap = paths ? paths->_child : 0; constraintMap; constraintMap = constraintMap->_next) { + int constraintIndex = 0, i; + Json* timelineMap; + + PathConstraintData* data = skeletonData->findPathConstraint(constraintMap->_name); + if (!data) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Path constraint not found: ", constraintMap->_name); + return NULL; + } + + for (i = 0; i < skeletonData->_pathConstraints.size(); i++) { + if (skeletonData->_pathConstraints[i] == data) { + constraintIndex = i; + break; + } + } + + for (timelineMap = constraintMap->_child; timelineMap; timelineMap = timelineMap->_next) { + const char* timelineName = timelineMap->_name; + if (strcmp(timelineName, "position") == 0 || strcmp(timelineName, "spacing") == 0) { + PathConstraintPositionTimeline* timeline; + float timelineScale = 1; + if (strcmp(timelineName, "spacing") == 0) { + timeline = NEW(PathConstraintSpacingTimeline); + new (timeline) PathConstraintSpacingTimeline(timelineMap->_size); + + if (data->_spacingMode == SpacingMode_Length || data->_spacingMode == SpacingMode_Fixed) { + timelineScale = _scale; + } + } + else { + timeline = NEW(PathConstraintPositionTimeline); + new (timeline) PathConstraintPositionTimeline(timelineMap->_size); + + if (data->_positionMode == PositionMode_Fixed) { + timelineScale = _scale; + } + } + + timeline->_pathConstraintIndex = constraintIndex; + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, timelineName, 0) * timelineScale); + readCurve(valueMap, timeline, frameIndex); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[(timelineMap->_size - 1) * PathConstraintPositionTimeline::ENTRIES]); + } + else if (strcmp(timelineName, "mix") == 0) { + PathConstraintMixTimeline* timeline = NEW(PathConstraintMixTimeline); + new (timeline) PathConstraintMixTimeline(timelineMap->_size); + timeline->_pathConstraintIndex = constraintIndex; + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), Json::getFloat(valueMap, "rotateMix", 1), Json::getFloat(valueMap, "translateMix", 1)); + readCurve(valueMap, timeline, frameIndex); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[(timelineMap->_size - 1) * PathConstraintMixTimeline::ENTRIES]); + } + } + } + + /** Deform timelines. */ + for (constraintMap = deform ? deform->_child : NULL; constraintMap; constraintMap = constraintMap->_next) { + Skin* skin = skeletonData->findSkin(constraintMap->_name); + for (slotMap = constraintMap->_child; slotMap; slotMap = slotMap->_next) { + int slotIndex = skeletonData->findSlotIndex(slotMap->_name); + Json* timelineMap; + for (timelineMap = slotMap->_child; timelineMap; timelineMap = timelineMap->_next) { + DeformTimeline *timeline; + int weighted, deformLength; + + Attachment* baseAttachment = skin->getAttachment(slotIndex, timelineMap->_name); + + if (!baseAttachment) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Attachment not found: ", timelineMap->_name); + return NULL; + } + + VertexAttachment* attachment = static_cast(baseAttachment); + + weighted = attachment->_bones.size() != 0; + Vector& vertices = attachment->_vertices; + deformLength = weighted ? static_cast(vertices.size()) / 3 * 2 : static_cast(vertices.size()); + Vector tempDeform; + tempDeform.reserve(deformLength); + for (int i = 0; i < deformLength; ++i) { + tempDeform.push_back(0); + } + + timeline = NEW(DeformTimeline); + new (timeline) DeformTimeline(timelineMap->_size); + + timeline->_slotIndex = slotIndex; + timeline->_attachment = attachment; + + for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + Json* vertices = Json::getItem(valueMap, "vertices"); + Vector deform; + if (!vertices) { + if (weighted) { + deform = tempDeform; + } + else { + deform = attachment->_vertices; + } + } + else { + int v, start = Json::getInt(valueMap, "offset", 0); + Json* vertex; + deform = tempDeform; + if (_scale == 1) { + for (vertex = vertices->_child, v = start; vertex; vertex = vertex->_next, ++v) { + deform[v] = vertex->_valueFloat; + } + } + else { + for (vertex = vertices->_child, v = start; vertex; vertex = vertex->_next, ++v) { + deform[v] = vertex->_valueFloat * _scale; + } + } + if (!weighted) { + Vector& verticesAttachment = attachment->_vertices; + for (v = 0; v < deformLength; ++v) { + deform[v] += verticesAttachment[v]; + } + } + } + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), deform); + readCurve(valueMap, timeline, frameIndex); + } + + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[timelineMap->_size - 1]); + } + } + } + + /** Draw order timeline. */ + if (drawOrder) { + DrawOrderTimeline* timeline = NEW(DrawOrderTimeline); + new (timeline) DrawOrderTimeline(drawOrder->_size); + + for (valueMap = drawOrder->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + int ii; + Vector drawOrder2; + Json* offsets = Json::getItem(valueMap, "offsets"); + if (offsets) { + Json* offsetMap; + Vector unchanged; + unchanged.reserve(skeletonData->_slots.size() - offsets->_size); + unchanged.setSize(skeletonData->_slots.size() - offsets->_size); + int originalIndex = 0, unchangedIndex = 0; + + drawOrder2.reserve(skeletonData->_slots.size()); + drawOrder2.setSize(skeletonData->_slots.size()); + for (ii = static_cast(skeletonData->_slots.size()) - 1; ii >= 0; --ii) { + drawOrder2[ii] = -1; + } + + for (offsetMap = offsets->_child; offsetMap; offsetMap = offsetMap->_next) { + int slotIndex = skeletonData->findSlotIndex(Json::getString(offsetMap, "slot", 0)); + if (slotIndex == -1) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Slot not found: ", Json::getString(offsetMap, "slot", 0)); + return NULL; + } + /* Collect unchanged items. */ + while (originalIndex != slotIndex) { + unchanged[unchangedIndex++] = originalIndex++; + } + /* Set changed items. */ + drawOrder2[originalIndex + Json::getInt(offsetMap, "offset", 0)] = originalIndex; + originalIndex++; + } + /* Collect remaining unchanged items. */ + while (originalIndex < skeletonData->_slots.size()) { + unchanged[unchangedIndex++] = originalIndex++; + } + /* Fill in unchanged items. */ + for (ii = static_cast(skeletonData->_slots.size()) - 1; ii >= 0; ii--) { + if (drawOrder2[ii] == -1) { + drawOrder2[ii] = unchanged[--unchangedIndex]; + } + } + } + timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0), drawOrder2); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[drawOrder->_size - 1]); + } + + /** Event timeline. */ + if (events) { + EventTimeline* timeline = NEW(EventTimeline); + new (timeline) EventTimeline(events->_size); + + for (valueMap = events->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) { + Event* event; + EventData* eventData = skeletonData->findEvent(Json::getString(valueMap, "name", 0)); + if (!eventData) { + ContainerUtil::cleanUpVectorOfPointers(timelines); + setError(NULL, "Event not found: ", Json::getString(valueMap, "name", 0)); + return NULL; + } + + event = NEW(Event); + new (event) Event(Json::getFloat(valueMap, "time", 0), *eventData); + event->_intValue = Json::getInt(valueMap, "int", eventData->_intValue); + event->_floatValue = Json::getFloat(valueMap, "float", eventData->_floatValue); + event->_stringValue = Json::getString(valueMap, "string", eventData->_stringValue.c_str()); + timeline->setFrame(frameIndex, event); + } + timelines.push_back(timeline); + timelinesCount++; + duration = MAX(duration, timeline->_frames[events->_size - 1]); + } + + Animation* ret = NEW(Animation); + new (ret) Animation(std::string(root->_name), timelines, duration); + + return ret; + } + + void SkeletonJson::readVertices(Json* attachmentMap, VertexAttachment* attachment, int verticesLength) { + Json* entry; + int i, n, nn, entrySize; + Vector vertices; + + attachment->setWorldVerticesLength(verticesLength); + + entry = Json::getItem(attachmentMap, "vertices"); + entrySize = entry->_size; + vertices.reserve(entrySize); + vertices.setSize(entrySize); + for (entry = entry->_child, i = 0; entry; entry = entry->_next, ++i) { + vertices[i] = entry->_valueFloat; + } + + if (verticesLength == entrySize) { + if (_scale != 1) { + for (i = 0; i < entrySize; ++i) { + vertices[i] *= _scale; + } + } + + attachment->setVertices(vertices); + return; + } + + Vertices bonesAndWeights; + bonesAndWeights._bones.reserve(verticesLength * 3); + bonesAndWeights._vertices.reserve(verticesLength * 3 * 3); + + for (i = 0, n = entrySize; i < n;) { + int boneCount = (int)vertices[i++]; + bonesAndWeights._bones.push_back(boneCount); + for (nn = i + boneCount * 4; i < nn; i += 4) { + bonesAndWeights._bones.push_back((int)vertices[i]); + bonesAndWeights._vertices.push_back(vertices[i + 1] * _scale); + bonesAndWeights._vertices.push_back(vertices[i + 2] * _scale); + bonesAndWeights._vertices.push_back(vertices[i + 3]); + } + } + + attachment->setVertices(bonesAndWeights._vertices); + attachment->setBones(bonesAndWeights._bones); + } + + void SkeletonJson::setError(Json* root, const char* value1, const char* value2) { + char message[256]; + int length; + strcpy(message, value1); + length = (int)strlen(value1); + if (value2) { + strncat(message + length, value2, 255 - length); + } + + _error = std::string(message); + + if (root) { + DESTROY(Json, root); + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Skin.cpp b/spine-cpp/spine-cpp/src/spine/Skin.cpp new file mode 100644 index 000000000..12c80620a --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Skin.cpp @@ -0,0 +1,118 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include + +#include + +namespace Spine { + Skin::AttachmentKey::AttachmentKey(int slotIndex, std::string name) : + _slotIndex(slotIndex), + _name(name) { + // Empty + } + + bool Skin::AttachmentKey::operator==(const AttachmentKey &other) const { + return _slotIndex == other._slotIndex && _name == other._name; + } + + std::size_t Skin::HashAttachmentKey::operator()(const Spine::Skin::AttachmentKey& val) const { + std::size_t h1 = val._slotIndex; + + return h1; + } + + Skin::Skin(std::string name) : _name(name) { + assert(_name.length() > 0); + } + + void Skin::addAttachment(int slotIndex, std::string name, Attachment* attachment) { + assert(attachment); + + _attachments.insert(AttachmentKey(slotIndex, name), attachment); + } + + Attachment* Skin::getAttachment(int slotIndex, std::string name) { + HashMap::Iterator i = _attachments.find(AttachmentKey(slotIndex, name)); + + Attachment* ret = NULL; + + if (i != _attachments.end()) { + ret = i.second(); + } + + return ret; + } + + void Skin::findNamesForSlot(int slotIndex, Vector& names) { + for (HashMap::Iterator i = _attachments.begin(); i != _attachments.end(); ++i) { + if (i.first()._slotIndex == slotIndex) { + names.push_back(i.first()._name); + } + } + } + + void Skin::findAttachmentsForSlot(int slotIndex, Vector& attachments) { + for (HashMap::Iterator i = _attachments.begin(); i != _attachments.end(); ++i) { + if (i.first()._slotIndex == slotIndex) { + attachments.push_back(i.second()); + } + } + } + + const std::string& Skin::getName() { + return _name; + } + + HashMap& Skin::getAttachments() { + return _attachments; + } + + void Skin::attachAll(Skeleton& skeleton, Skin& oldSkin) { + Vector& slots = skeleton.getSlots(); + + for (HashMap::Iterator i = oldSkin.getAttachments().begin(); i != oldSkin.getAttachments().end(); ++i) { + int slotIndex = i.first()._slotIndex; + Slot* slot = slots[slotIndex]; + + if (slot->getAttachment() == i.second()) { + Attachment* attachment = NULL; + if ((attachment = getAttachment(slotIndex, i.first()._name))) { + slot->setAttachment(attachment); + } + } + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Slot.cpp b/spine-cpp/spine-cpp/src/spine/Slot.cpp new file mode 100644 index 000000000..9ea7c7651 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Slot.cpp @@ -0,0 +1,177 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include +#include + +namespace Spine { + Slot::Slot(SlotData& data, Bone& bone) : + _data(data), + _bone(bone), + _skeleton(bone.getSkeleton()), + _r(1), + _g(1), + _b(1), + _a(1), + _r2(0), + _g2(0), + _b2(0), + _hasSecondColor(false), + _attachment(NULL), + _attachmentTime(0) { + setToSetupPose(); + } + + void Slot::setToSetupPose() { + _r = _data.getR(); + _g = _data.getG(); + _b = _data.getB(); + _a = _data.getA(); + + std::string attachmentName = _data.getAttachmentName(); + if (attachmentName.length() > 0) { + _attachment = NULL; + setAttachment(_skeleton.getAttachment(_data.getIndex(), attachmentName)); + } + else { + setAttachment(NULL); + } + } + + SlotData& Slot::getData() { + return _data; + } + + Bone& Slot::getBone() { + return _bone; + } + + Skeleton& Slot::getSkeleton() { + return _skeleton; + } + + float Slot::getR() { + return _r; + } + + void Slot::setR(float inValue) { + _r = inValue; + } + + float Slot::getG() { + return _g; + } + + void Slot::setG(float inValue) { + _g = inValue; + } + + float Slot::getB() { + return _b; + } + + void Slot::setB(float inValue) { + _b = inValue; + } + + float Slot::getA() { + return _a; + } + + void Slot::setA(float inValue) { + _a = inValue; + } + + float Slot::getR2() { + return _r2; + } + + void Slot::setR2(float inValue) { + _r2 = inValue; + } + + float Slot::getG2() { + return _g2; + } + + void Slot::setG2(float inValue) { + _g2 = inValue; + } + + float Slot::getB2() { + return _b2; + } + + void Slot::setB2(float inValue) { + _b2 = inValue; + } + + bool Slot::hasSecondColor() { + return _hasSecondColor; + } + + void Slot::setHasSecondColor(bool inValue) { + _hasSecondColor = inValue; + } + + Attachment* Slot::getAttachment() { + return _attachment; + } + + void Slot::setAttachment(Attachment* inValue) { + if (_attachment == inValue) { + return; + } + + _attachment = inValue; + _attachmentTime = _skeleton.getTime(); + _attachmentVertices.clear(); + } + + float Slot::getAttachmentTime() { + return _skeleton.getTime() - _attachmentTime; + } + + void Slot::setAttachmentTime(float inValue) { + _attachmentTime = _skeleton.getTime() - inValue; + } + + Vector& Slot::getAttachmentVertices() { + return _attachmentVertices; + } + + void Slot::setAttachmentVertices(Vector inValue) { + _attachmentVertices = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/SlotData.cpp b/spine-cpp/spine-cpp/src/spine/SlotData.cpp new file mode 100644 index 000000000..e8d517905 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/SlotData.cpp @@ -0,0 +1,146 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + SlotData::SlotData(int index, std::string name, BoneData& boneData) : + _index(index), + _name(name), + _boneData(boneData), + _r(1), + _g(1), + _b(1), + _a(1), + _r2(0), + _g2(0), + _b2(0), + _a2(1), + _hasSecondColor(false), + _attachmentName(), + _blendMode(BlendMode_Normal) { + assert(_index >= 0); + assert(_name.length() > 0); + } + + const int SlotData::getIndex() { + return _index; + } + + const std::string& SlotData::getName() { + return _name; + } + + BoneData& SlotData::getBoneData() { + return _boneData; + } + + float SlotData::getR() { + return _r; + } + + void SlotData::setR(float inValue) { + _r = inValue; + } + + float SlotData::getG() { + return _g; + } + + void SlotData::setG(float inValue) { + _g = inValue; + } + + float SlotData::getB() { + return _b; + } + + void SlotData::setB(float inValue) { + _b = inValue; + } + + float SlotData::getA() { + return _a; + } + + void SlotData::setA(float inValue) { + _a = inValue; + } + + float SlotData::getR2() { + return _r2; + } + + void SlotData::setR2(float inValue) { + _r2 = inValue; + } + + float SlotData::getG2() { + return _g2; + } + + void SlotData::setG2(float inValue) { + _g2 = inValue; + } + + float SlotData::getB2() { + return _b2; + } + + void SlotData::setB2(float inValue) { + _b2 = inValue; + } + + bool SlotData::hasSecondColor() { + return _hasSecondColor; + } + + void SlotData::setHasSecondColor(bool inValue) { + _hasSecondColor = inValue; + } + + std::string SlotData::getAttachmentName() { + return _attachmentName; + } + + void SlotData::setAttachmentName(std::string inValue) { + _attachmentName = inValue; + } + + BlendMode SlotData::getBlendMode() { + return _blendMode; + } + + void SlotData::setBlendMode(BlendMode inValue) { + _blendMode = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/TextureLoader.cpp b/spine-cpp/spine-cpp/src/spine/TextureLoader.cpp new file mode 100644 index 000000000..8658f8af9 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/TextureLoader.cpp @@ -0,0 +1,41 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +namespace Spine { + TextureLoader::TextureLoader() { + // Empty + } + + TextureLoader::~TextureLoader() { + // Empty + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Timeline.cpp b/spine-cpp/spine-cpp/src/spine/Timeline.cpp new file mode 100644 index 000000000..106a2896b --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Timeline.cpp @@ -0,0 +1,46 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +namespace Spine { + RTTI_IMPL_NOPARENT(Timeline); + + Timeline::Timeline() { + // Empty + } + + Timeline::~Timeline() { + // Empty + } +} diff --git a/spine-cpp/spine-cpp/src/spine/TransformConstraint.cpp b/spine-cpp/spine-cpp/src/spine/TransformConstraint.cpp new file mode 100644 index 000000000..267e1dd17 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/TransformConstraint.cpp @@ -0,0 +1,382 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +namespace Spine { + RTTI_IMPL(TransformConstraint, Constraint); + + TransformConstraint::TransformConstraint(TransformConstraintData& data, Skeleton& skeleton) : Constraint(), + _data(data), + _target(skeleton.findBone(data.getTarget()->getName())), + _rotateMix(data.getRotateMix()), + _translateMix(data.getTranslateMix()), + _scaleMix(data.getScaleMix()), + _shearMix(data.getShearMix()) { + _bones.reserve(_data.getBones().size()); + for (BoneData** i = _data.getBones().begin(); i != _data.getBones().end(); ++i) { + BoneData* boneData = (*i); + + _bones.push_back(skeleton.findBone(boneData->getName())); + } + } + + void TransformConstraint::apply() { + update(); + } + + void TransformConstraint::update() { + if (_data.isLocal()) { + if (_data.isRelative()) { + applyRelativeLocal(); + } + else { + applyAbsoluteLocal(); + } + } + else { + if (_data.isRelative()) { + applyRelativeWorld(); + } + else { + applyAbsoluteWorld(); + } + } + } + + int TransformConstraint::getOrder() { + return _data.getOrder(); + } + + TransformConstraintData& TransformConstraint::getData() { + return _data; + } + + Vector& TransformConstraint::getBones() { + return _bones; + } + + Bone* TransformConstraint::getTarget() { + return _target; + } + + void TransformConstraint::setTarget(Bone* inValue) { + _target = inValue; + } + + float TransformConstraint::getRotateMix() { + return _rotateMix; + } + + void TransformConstraint::setRotateMix(float inValue) { + _rotateMix = inValue; + } + + float TransformConstraint::getTranslateMix() { + return _translateMix; + } + + void TransformConstraint::setTranslateMix(float inValue) { + _translateMix = inValue; + } + + float TransformConstraint::getScaleMix() { + return _scaleMix; + } + + void TransformConstraint::setScaleMix(float inValue) { + _scaleMix = inValue; + } + + float TransformConstraint::getShearMix() { + return _shearMix; + } + + void TransformConstraint::setShearMix(float inValue) { + _shearMix = inValue; + } + + void TransformConstraint::applyAbsoluteWorld() { + float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix; + Bone& target = *_target; + float ta = target._a, tb = target._b, tc = target._c, td = target._d; + float degRadReflect = ta * td - tb * tc > 0 ? DegRad : -DegRad; + float offsetRotation = _data._offsetRotation * degRadReflect, offsetShearY = _data._offsetShearY * degRadReflect; + + for (Bone** i = _bones.begin(); i != _bones.end(); ++i) { + Bone* item = (*i); + Bone& bone = *item; + + bool modified = false; + + if (rotateMix != 0) { + float a = bone._a, b = bone._b, c = bone._c, d = bone._d; + float r = MathUtil::atan2(tc, ta) - MathUtil::atan2(c, a) + offsetRotation; + if (r > SPINE_PI) { + r -= SPINE_PI_2; + } + else if (r < -SPINE_PI) { + r += SPINE_PI_2; + } + + r *= rotateMix; + float cos = MathUtil::cos(r), sin = MathUtil::sin(r); + bone._a = cos * a - sin * c; + bone._b = cos * b - sin * d; + bone._c = sin * a + cos * c; + bone._d = sin * b + cos * d; + modified = true; + } + + if (translateMix != 0) { + float tx, ty; + target.localToWorld(_data._offsetX, _data._offsetY, tx, ty); + bone._worldX += (tx - bone._worldX) * translateMix; + bone._worldY += (ty - bone._worldY) * translateMix; + modified = true; + } + + if (scaleMix > 0) { + float s = (float)sqrt(bone._a * bone._a + bone._c * bone._c); + + if (s > 0.00001f) { + s = (s + ((float)sqrt(ta * ta + tc * tc) - s + _data._offsetScaleX) * scaleMix) / s; + } + bone._a *= s; + bone._c *= s; + s = (float)sqrt(bone._b * bone._b + bone._d * bone._d); + + if (s > 0.00001f) { + s = (s + ((float)sqrt(tb * tb + td * td) - s + _data._offsetScaleY) * scaleMix) / s; + } + bone._b *= s; + bone._d *= s; + modified = true; + } + + if (shearMix > 0) { + float b = bone._b, d = bone._d; + float by = MathUtil::atan2(d, b); + float r = MathUtil::atan2(td, tb) - MathUtil::atan2(tc, ta) - (by - MathUtil::atan2(bone._c, bone._a)); + if (r > SPINE_PI) { + r -= SPINE_PI_2; + } + else if (r < -SPINE_PI) { + r += SPINE_PI_2; + } + + r = by + (r + offsetShearY) * shearMix; + float s = (float)sqrt(b * b + d * d); + bone._b = MathUtil::cos(r) * s; + bone._d = MathUtil::sin(r) * s; + modified = true; + } + + if (modified) { + bone._appliedValid = false; + } + } + } + + void TransformConstraint::applyRelativeWorld() { + float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix; + Bone& target = *_target; + float ta = target._a, tb = target._b, tc = target._c, td = target._d; + float degRadReflect = ta * td - tb * tc > 0 ? DegRad : -DegRad; + float offsetRotation = _data._offsetRotation * degRadReflect, offsetShearY = _data._offsetShearY * degRadReflect; + for (Bone** i = _bones.begin(); i != _bones.end(); ++i) { + Bone* item = (*i); + Bone& bone = *item; + + bool modified = false; + + if (rotateMix != 0) { + float a = bone._a, b = bone._b, c = bone._c, d = bone._d; + float r = MathUtil::atan2(tc, ta) + offsetRotation; + if (r > SPINE_PI) { + r -= SPINE_PI_2; + } + else if (r < -SPINE_PI) { + r += SPINE_PI_2; + } + + r *= rotateMix; + float cos = MathUtil::cos(r), sin = MathUtil::sin(r); + bone._a = cos * a - sin * c; + bone._b = cos * b - sin * d; + bone._c = sin * a + cos * c; + bone._d = sin * b + cos * d; + modified = true; + } + + if (translateMix != 0) { + float tx, ty; + target.localToWorld(_data._offsetX, _data._offsetY, tx, ty); + bone._worldX += tx * translateMix; + bone._worldY += ty * translateMix; + modified = true; + } + + if (scaleMix > 0) { + float s = ((float)sqrt(ta * ta + tc * tc) - 1 + _data._offsetScaleX) * scaleMix + 1; + bone._a *= s; + bone._c *= s; + s = ((float)sqrt(tb * tb + td * td) - 1 + _data._offsetScaleY) * scaleMix + 1; + bone._b *= s; + bone._d *= s; + modified = true; + } + + if (shearMix > 0) { + float r = MathUtil::atan2(td, tb) - MathUtil::atan2(tc, ta); + if (r > SPINE_PI) { + r -= SPINE_PI_2; + } + else if (r < -SPINE_PI) { + r += SPINE_PI_2; + } + + float b = bone._b, d = bone._d; + r = MathUtil::atan2(d, b) + (r - SPINE_PI / 2 + offsetShearY) * shearMix; + float s = (float)sqrt(b * b + d * d); + bone._b = MathUtil::cos(r) * s; + bone._d = MathUtil::sin(r) * s; + modified = true; + } + + if (modified) { + bone._appliedValid = false; + } + } + } + + void TransformConstraint::applyAbsoluteLocal() { + float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix; + Bone& target = *_target; + if (!target._appliedValid) { + target.updateAppliedTransform(); + } + + for (Bone** i = _bones.begin(); i != _bones.end(); ++i) { + Bone* item = (*i); + Bone& bone = *item; + + if (!bone._appliedValid) { + bone.updateAppliedTransform(); + } + + float rotation = bone._arotation; + if (rotateMix != 0) { + float r = target._arotation - rotation + _data._offsetRotation; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + rotation += r * rotateMix; + } + + float x = bone._ax, y = bone._ay; + if (translateMix != 0) { + x += (target._ax - x + _data._offsetX) * translateMix; + y += (target._ay - y + _data._offsetY) * translateMix; + } + + float scaleX = bone._ascaleX, scaleY = bone._ascaleY; + if (scaleMix > 0) { + if (scaleX > 0.00001f) { + scaleX = (scaleX + (target._ascaleX - scaleX + _data._offsetScaleX) * scaleMix) / scaleX; + } + + if (scaleY > 0.00001f) { + scaleY = (scaleY + (target._ascaleY - scaleY + _data._offsetScaleY) * scaleMix) / scaleY; + } + } + + float shearY = bone._ashearY; + if (shearMix > 0) { + float r = target._ashearY - shearY + _data._offsetShearY; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + bone._shearY += r * shearMix; + } + + bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY); + } + } + + void TransformConstraint::applyRelativeLocal() { + float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix; + Bone& target = *_target; + if (!target._appliedValid) { + target.updateAppliedTransform(); + } + + for (Bone** i = _bones.begin(); i != _bones.end(); ++i) { + Bone* item = (*i); + Bone& bone = *item; + + if (!bone._appliedValid) { + bone.updateAppliedTransform(); + } + + float rotation = bone._arotation; + if (rotateMix != 0) { + rotation += (target._arotation + _data._offsetRotation) * rotateMix; + } + + float x = bone._ax, y = bone._ay; + if (translateMix != 0) { + x += (target._ax + _data._offsetX) * translateMix; + y += (target._ay + _data._offsetY) * translateMix; + } + + float scaleX = bone._ascaleX, scaleY = bone._ascaleY; + if (scaleMix > 0) { + if (scaleX > 0.00001f) { + scaleX *= ((target._ascaleX - 1 + _data._offsetScaleX) * scaleMix) + 1; + } + + if (scaleY > 0.00001f) { + scaleY *= ((target._ascaleY - 1 + _data._offsetScaleY) * scaleMix) + 1; + } + } + + float shearY = bone._ashearY; + if (shearMix > 0) { + shearY += (target._ashearY + _data._offsetShearY) * shearMix; + } + + bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY); + } + } +} diff --git a/spine-cpp/spine-cpp/src/spine/TransformConstraintData.cpp b/spine-cpp/spine-cpp/src/spine/TransformConstraintData.cpp new file mode 100644 index 000000000..fb3f5be91 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/TransformConstraintData.cpp @@ -0,0 +1,119 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +#include + +namespace Spine { + TransformConstraintData::TransformConstraintData(std::string name) : + _name(name), + _order(0), + _target(NULL), + _rotateMix(0), + _translateMix(0), + _scaleMix(0), + _shearMix(0), + _offsetRotation(0), + _offsetX(0), + _offsetY(0), + _offsetScaleX(0), + _offsetScaleY(0), + _offsetShearY(0), + _relative(false), + _local(false) { + assert(_name.length() > 0); + } + + const std::string& TransformConstraintData::getName() { + return _name; + } + + int TransformConstraintData::getOrder() { + return _order; + } + Vector& TransformConstraintData::getBones() { + return _bones; + } + + BoneData* TransformConstraintData::getTarget() { + return _target; + } + + float TransformConstraintData::getRotateMix() { + return _rotateMix; + } + + float TransformConstraintData::getTranslateMix() { + return _translateMix; + } + + float TransformConstraintData::getScaleMix() { + return _scaleMix; + } + + float TransformConstraintData::getShearMix() { + return _shearMix; + } + + float TransformConstraintData::getOffsetRotation() { + return _offsetRotation; + } + + float TransformConstraintData::getOffsetX() { + return _offsetX; + } + + float TransformConstraintData::getOffsetY() { + return _offsetY; + } + + float TransformConstraintData::getOffsetScaleX() { + return _offsetScaleX; + } + + float TransformConstraintData::getOffsetScaleY() { + return _offsetScaleY; + } + + float TransformConstraintData::getOffsetShearY() { + return _offsetShearY; + } + + bool TransformConstraintData::isRelative() { + return _relative; + } + + bool TransformConstraintData::isLocal() { + return _local; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp b/spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp new file mode 100644 index 000000000..60b11f6eb --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp @@ -0,0 +1,139 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(TransformConstraintTimeline, CurveTimeline); + + const int TransformConstraintTimeline::ENTRIES = 5; + const int TransformConstraintTimeline::PREV_TIME = -5; + const int TransformConstraintTimeline::PREV_ROTATE = -4; + const int TransformConstraintTimeline::PREV_TRANSLATE = -3; + const int TransformConstraintTimeline::PREV_SCALE = -2; + const int TransformConstraintTimeline::PREV_SHEAR = -1; + const int TransformConstraintTimeline::ROTATE = 1; + const int TransformConstraintTimeline::TRANSLATE = 2; + const int TransformConstraintTimeline::SCALE = 3; + const int TransformConstraintTimeline::SHEAR = 4; + + TransformConstraintTimeline::TransformConstraintTimeline(int frameCount) : CurveTimeline(frameCount), _transformConstraintIndex(0) { + _frames.reserve(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES); + } + + void TransformConstraintTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + TransformConstraint* constraintP = skeleton._transformConstraints[_transformConstraintIndex]; + TransformConstraint& constraint = *constraintP; + + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + constraint._rotateMix = constraint._data._rotateMix; + constraint._translateMix = constraint._data._translateMix; + constraint._scaleMix = constraint._data._scaleMix; + constraint._shearMix = constraint._data._shearMix; + return; + case MixPose_Current: + constraint._rotateMix += (constraint._data._rotateMix - constraint._rotateMix) * alpha; + constraint._translateMix += (constraint._data._translateMix - constraint._translateMix) * alpha; + constraint._scaleMix += (constraint._data._scaleMix - constraint._scaleMix) * alpha; + constraint._shearMix += (constraint._data._shearMix - constraint._shearMix) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float rotate, translate, scale, shear; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + int i = static_cast(_frames.size()); + rotate = _frames[i + PREV_ROTATE]; + translate = _frames[i + PREV_TRANSLATE]; + scale = _frames[i + PREV_SCALE]; + shear = _frames[i + PREV_SHEAR]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + rotate = _frames[frame + PREV_ROTATE]; + translate = _frames[frame + PREV_TRANSLATE]; + scale = _frames[frame + PREV_SCALE]; + shear = _frames[frame + PREV_SHEAR]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + rotate += (_frames[frame + ROTATE] - rotate) * percent; + translate += (_frames[frame + TRANSLATE] - translate) * percent; + scale += (_frames[frame + SCALE] - scale) * percent; + shear += (_frames[frame + SHEAR] - shear) * percent; + } + + if (pose == MixPose_Setup) { + TransformConstraintData& data = constraint._data; + constraint._rotateMix = data._rotateMix + (rotate - data._rotateMix) * alpha; + constraint._translateMix = data._translateMix + (translate - data._translateMix) * alpha; + constraint._scaleMix = data._scaleMix + (scale - data._scaleMix) * alpha; + constraint._shearMix = data._shearMix + (shear - data._shearMix) * alpha; + } + else { + constraint._rotateMix += (rotate - constraint._rotateMix) * alpha; + constraint._translateMix += (translate - constraint._translateMix) * alpha; + constraint._scaleMix += (scale - constraint._scaleMix) * alpha; + constraint._shearMix += (shear - constraint._shearMix) * alpha; + } + } + + int TransformConstraintTimeline::getPropertyId() { + return ((int)TimelineType_TransformConstraint << 24) + _transformConstraintIndex; + } + + void TransformConstraintTimeline::setFrame(int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + ROTATE] = rotateMix; + _frames[frameIndex + TRANSLATE] = translateMix; + _frames[frameIndex + SCALE] = scaleMix; + _frames[frameIndex + SHEAR] = shearMix; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp b/spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp new file mode 100644 index 000000000..b5ceccc75 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp @@ -0,0 +1,116 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(TranslateTimeline, CurveTimeline); + + const int TranslateTimeline::ENTRIES = 3; + const int TranslateTimeline::PREV_TIME = -3; + const int TranslateTimeline::PREV_X = -2; + const int TranslateTimeline::PREV_Y = -1; + const int TranslateTimeline::X = 1; + const int TranslateTimeline::Y = 2; + + TranslateTimeline::TranslateTimeline(int frameCount) : CurveTimeline(frameCount), _boneIndex(0) { + _frames.reserve(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES); + } + + void TranslateTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + Bone* boneP = skeleton._bones[_boneIndex]; + Bone& bone = *boneP; + + if (time < _frames[0]) { + switch (pose) { + case MixPose_Setup: + bone._x = bone._data._x; + bone._y = bone._data._y; + return; + case MixPose_Current: + bone._x += (bone._data._x - bone._x) * alpha; + bone._y += (bone._data._y - bone._y) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float x, y; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + x = _frames[_frames.size() + PREV_X]; + y = _frames[_frames.size() + PREV_Y]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + x = _frames[frame + PREV_X]; + y = _frames[frame + PREV_Y]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + x += (_frames[frame + X] - x) * percent; + y += (_frames[frame + Y] - y) * percent; + } + + if (pose == MixPose_Setup) { + bone._x = bone._data._x + x * alpha; + bone._y = bone._data._y + y * alpha; + } + else { + bone._x += (bone._data._x + x - bone._x) * alpha; + bone._y += (bone._data._y + y - bone._y) * alpha; + } + } + + int TranslateTimeline::getPropertyId() { + return ((int)TimelineType_Translate << 24) + _boneIndex; + } + + void TranslateTimeline::setFrame(int frameIndex, float time, float x, float y) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + X] = x; + _frames[frameIndex + Y] = y; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Triangulator.cpp b/spine-cpp/spine-cpp/src/spine/Triangulator.cpp new file mode 100644 index 000000000..3006cda54 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Triangulator.cpp @@ -0,0 +1,296 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +namespace Spine { + Vector& Triangulator::triangulate(Vector& vertices) { + int vertexCount = static_cast(vertices.size() >> 1); + + Vector& indices = _indices; + indices.clear(); + indices.reserve(vertexCount); + indices.setSize(vertexCount); + for (int i = 0; i < vertexCount; ++i) { + indices[i] = i; + } + + Vector& isConcaveArray = _isConcaveArray; + isConcaveArray.reserve(vertexCount); + isConcaveArray.setSize(vertexCount); + for (int i = 0, n = vertexCount; i < n; ++i) { + isConcaveArray[i] = isConcave(i, vertexCount, vertices, indices); + } + + Vector& triangles = _triangles; + triangles.clear(); + triangles.reserve(MAX(0, vertexCount - 2) << 2); + + while (vertexCount > 3) { + // Find ear tip. + int previous = vertexCount - 1, i = 0, next = 1; + + // outer: + while (true) { + if (!isConcaveArray[i]) { + int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; + float p1x = vertices[p1], p1y = vertices[p1 + 1]; + float p2x = vertices[p2], p2y = vertices[p2 + 1]; + float p3x = vertices[p3], p3y = vertices[p3 + 1]; + for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { + if (!isConcaveArray[ii]) { + continue; + } + + int v = indices[ii] << 1; + float& vx = vertices[v], vy = vertices[v + 1]; + if (positiveArea(p3x, p3y, p1x, p1y, vx, vy)) { + if (positiveArea(p1x, p1y, p2x, p2y, vx, vy)) { + if (positiveArea(p2x, p2y, p3x, p3y, vx, vy)) { + goto break_outer; // break outer; + } + } + } + } + break; + } + break_outer: + + if (next == 0) { + do { + if (!isConcaveArray[i]) { + break; + } + i--; + } while (i > 0); + break; + } + + previous = i; + i = next; + next = (next + 1) % vertexCount; + } + + // Cut ear tip. + triangles.push_back(indices[(vertexCount + i - 1) % vertexCount]); + triangles.push_back(indices[i]); + triangles.push_back(indices[(i + 1) % vertexCount]); + indices.erase(i); + isConcaveArray.erase(i); + vertexCount--; + + int previousIndex = (vertexCount + i - 1) % vertexCount; + int nextIndex = i == vertexCount ? 0 : i; + isConcaveArray[previousIndex] = isConcave(previousIndex, vertexCount, vertices, indices); + isConcaveArray[nextIndex] = isConcave(nextIndex, vertexCount, vertices, indices); + } + + if (vertexCount == 3) { + triangles.push_back(indices[2]); + triangles.push_back(indices[0]); + triangles.push_back(indices[1]); + } + + return triangles; + } + + Vector< Vector* > Triangulator::decompose(Vector& vertices, Vector& triangles) { + Vector< Vector* >&convexPolygons = _convexPolygons; + for (size_t i = 0, n = convexPolygons.size(); i < n; ++i) { + _polygonPool.free(convexPolygons[i]); + } + convexPolygons.clear(); + + Vector< Vector* > convexPolygonsIndices = _convexPolygonsIndices; + for (size_t i = 0, n = convexPolygonsIndices.size(); i < n; ++i) { + _polygonIndicesPool.free(convexPolygonsIndices[i]); + } + convexPolygonsIndices.clear(); + + Vector* polygonIndicesP = _polygonIndicesPool.obtain(); + Vector& polygonIndices = *polygonIndicesP; + polygonIndices.clear(); + + Vector* polygonP = _polygonPool.obtain(); + Vector& polygon = *polygonP; + polygon.clear(); + + // Merge subsequent triangles if they form a triangle fan. + int fanBaseIndex = -1, lastwinding = 0; + for (size_t i = 0, n = triangles.size(); i < n; i += 3) { + int t1 = triangles[i] << 1, t2 = triangles[i + 1] << 1, t3 = triangles[i + 2] << 1; + float x1 = vertices[t1], y1 = vertices[t1 + 1]; + float x2 = vertices[t2], y2 = vertices[t2 + 1]; + float x3 = vertices[t3], y3 = vertices[t3 + 1]; + + // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). + bool merged = false; + if (fanBaseIndex == t1) { + size_t o = polygon.size() - 4; + Vector& p = polygon; + int winding1 = winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3); + int winding2 = winding(x3, y3, p[0], p[1], p[2], p[3]); + if (winding1 == lastwinding && winding2 == lastwinding) { + polygon.push_back(x3); + polygon.push_back(y3); + polygonIndices.push_back(t3); + merged = true; + } + } + + // Otherwise make this triangle the new base. + if (!merged) { + if (polygon.size() > 0) { + convexPolygons.push_back(&polygon); + convexPolygonsIndices.push_back(&polygonIndices); + } + else { + _polygonPool.free(&polygon); + _polygonIndicesPool.free(&polygonIndices); + } + + polygon = *_polygonPool.obtain(); + polygon.clear(); + polygon.push_back(x1); + polygon.push_back(y1); + polygon.push_back(x2); + polygon.push_back(y2); + polygon.push_back(x3); + polygon.push_back(y3); + polygonIndices = *_polygonIndicesPool.obtain(); + polygonIndices.clear(); + polygonIndices.push_back(t1); + polygonIndices.push_back(t2); + polygonIndices.push_back(t3); + lastwinding = winding(x1, y1, x2, y2, x3, y3); + fanBaseIndex = t1; + } + } + + if (polygon.size() > 0) { + convexPolygons.push_back(&polygon); + convexPolygonsIndices.push_back(&polygonIndices); + } + + // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans. + for (size_t i = 0, n = convexPolygons.size(); i < n; ++i) { + polygonIndicesP = convexPolygonsIndices[i]; + polygonIndices = *polygonIndicesP; + + if (polygonIndices.size() == 0) continue; + int firstIndex = polygonIndices[0]; + int lastIndex = polygonIndices[polygonIndices.size() - 1]; + + polygon = *convexPolygons[i]; + size_t o = polygon.size() - 4; + Vector& p = polygon; + float prevPrevX = p[o], prevPrevY = p[o + 1]; + float prevX = p[o + 2], prevY = p[o + 3]; + float firstX = p[0], firstY = p[1]; + float secondX = p[2], secondY = p[3]; + int winding0 = winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); + + for (int ii = 0; ii < n; ++ii) { + if (ii == i) { + continue; + } + + Vector* otherIndicesP = convexPolygonsIndices[ii]; + Vector& otherIndices = *otherIndicesP; + + if (otherIndices.size() != 3) { + continue; + } + + int otherFirstIndex = otherIndices[0]; + int otherSecondIndex = otherIndices[1]; + int otherLastIndex = otherIndices[2]; + + Vector* otherPolyP = convexPolygons[ii]; + Vector& otherPoly = *otherPolyP; + + float x3 = otherPoly[otherPoly.size() - 2], y3 = otherPoly[otherPoly.size() - 1]; + + if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) { + continue; + } + + int winding1 = winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); + int winding2 = winding(x3, y3, firstX, firstY, secondX, secondY); + if (winding1 == winding0 && winding2 == winding0) { + otherPoly.clear(); + otherIndices.clear(); + polygon.push_back(x3); + polygon.push_back(y3); + polygonIndices.push_back(otherLastIndex); + prevPrevX = prevX; + prevPrevY = prevY; + prevX = x3; + prevY = y3; + ii = 0; + } + } + } + + // Remove empty polygons that resulted from the merge step above. + for (int i = static_cast(convexPolygons.size()) - 1; i >= 0; --i) { + polygon = *convexPolygons[i]; + if (polygon.size() == 0) { + convexPolygons.erase(i); + _polygonPool.free(&polygon); + polygonIndices = *convexPolygonsIndices[i]; + convexPolygonsIndices.erase(i); + _polygonIndicesPool.free(&polygonIndices); + } + } + + return convexPolygons; + } + + bool Triangulator::isConcave(int index, int vertexCount, Vector& vertices, Vector& indices) { + int previous = indices[(vertexCount + index - 1) % vertexCount] << 1; + int current = indices[index] << 1; + int next = indices[(index + 1) % vertexCount] << 1; + + return !positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], vertices[next + 1]); + } + + bool Triangulator::positiveArea(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; + } + + int Triangulator::winding(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + float px = p2x - p1x, py = p2y - p1y; + + return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp b/spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp new file mode 100644 index 000000000..9f824e70d --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp @@ -0,0 +1,196 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Spine { + RTTI_IMPL(TwoColorTimeline, CurveTimeline); + + const int TwoColorTimeline::ENTRIES = 8; + const int TwoColorTimeline::PREV_TIME = -8; + const int TwoColorTimeline::PREV_R = -7; + const int TwoColorTimeline::PREV_G = -6; + const int TwoColorTimeline::PREV_B = -5; + const int TwoColorTimeline::PREV_A = -4; + const int TwoColorTimeline::PREV_R2 = -3; + const int TwoColorTimeline::PREV_G2 = -2; + const int TwoColorTimeline::PREV_B2 = -1; + const int TwoColorTimeline::R = 1; + const int TwoColorTimeline::G = 2; + const int TwoColorTimeline::B = 3; + const int TwoColorTimeline::A = 4; + const int TwoColorTimeline::R2 = 5; + const int TwoColorTimeline::G2 = 6; + const int TwoColorTimeline::B2 = 7; + + TwoColorTimeline::TwoColorTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0) { + _frames.reserve(frameCount * ENTRIES); + _frames.setSize(frameCount * ENTRIES); + } + + void TwoColorTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector* pEvents, float alpha, MixPose pose, MixDirection direction) { + Slot* slotP = skeleton._slots[_slotIndex]; + Slot& slot = *slotP; + + if (time < _frames[0]) { + // Time is before first frame. + switch (pose) { + case MixPose_Setup: + slot._r = slot._data._r; + slot._g = slot._data._g; + slot._b = slot._data._b; + slot._a = slot._data._a; + slot._r2 = slot._data._r2; + slot._g2 = slot._data._g2; + slot._b2 = slot._data._b2; + return; + case MixPose_Current: + slot._r += (slot._r - slot._data._r) * alpha; + slot._g += (slot._g - slot._data._g) * alpha; + slot._b += (slot._b - slot._data._b) * alpha; + slot._a += (slot._a - slot._data._a) * alpha; + slot._r2 += (slot._r2 - slot._data._r2) * alpha; + slot._g2 += (slot._g2 - slot._data._g2) * alpha; + slot._b2 += (slot._b2 - slot._data._b2) * alpha; + return; + case MixPose_CurrentLayered: + default: + return; + } + } + + float r, g, b, a, r2, g2, b2; + if (time >= _frames[_frames.size() - ENTRIES]) { + // Time is after last frame. + int i = static_cast(_frames.size()); + r = _frames[i + PREV_R]; + g = _frames[i + PREV_G]; + b = _frames[i + PREV_B]; + a = _frames[i + PREV_A]; + r2 = _frames[i + PREV_R2]; + g2 = _frames[i + PREV_G2]; + b2 = _frames[i + PREV_B2]; + } + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation::binarySearch(_frames, time, ENTRIES); + r = _frames[frame + PREV_R]; + g = _frames[frame + PREV_G]; + b = _frames[frame + PREV_B]; + a = _frames[frame + PREV_A]; + r2 = _frames[frame + PREV_R2]; + g2 = _frames[frame + PREV_G2]; + b2 = _frames[frame + PREV_B2]; + float frameTime = _frames[frame]; + float percent = getCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime)); + + r += (_frames[frame + R] - r) * percent; + g += (_frames[frame + G] - g) * percent; + b += (_frames[frame + B] - b) * percent; + a += (_frames[frame + A] - a) * percent; + r2 += (_frames[frame + R2] - r2) * percent; + g2 += (_frames[frame + G2] - g2) * percent; + b2 += (_frames[frame + B2] - b2) * percent; + } + + if (alpha == 1) { + slot._r = r; + slot._g = g; + slot._b = b; + slot._a = a; + slot._r2 = r2; + slot._g2 = g2; + slot._b2 = b2; + } + else { + float br, bg, bb, ba, br2, bg2, bb2; + if (pose == MixPose_Setup) { + br = slot._data._r; + bg = slot._data._g; + bb = slot._data._b; + ba = slot._data._a; + br2 = slot._data._r2; + bg2 = slot._data._g2; + bb2 = slot._data._b2; + } + else { + br = slot._r; + bg = slot._g; + bb = slot._b; + ba = slot._a; + br2 = slot._r2; + bg2 = slot._g2; + bb2 = slot._b2; + } + + slot._r = br + ((r - br) * alpha); + slot._g = bg + ((g - bg) * alpha); + slot._b = bb + ((b - bb) * alpha); + slot._a = ba + ((a - ba) * alpha); + slot._r2 = br2 + ((r2 - br2) * alpha); + slot._g2 = bg2 + ((g2 - bg2) * alpha); + slot._b2 = bb2 + ((b2 - bb2) * alpha); + } + } + + int TwoColorTimeline::getPropertyId() { + return ((int)TimelineType_TwoColor << 24) + _slotIndex; + } + + void TwoColorTimeline::setFrame(int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) { + frameIndex *= ENTRIES; + _frames[frameIndex] = time; + _frames[frameIndex + R] = r; + _frames[frameIndex + G] = g; + _frames[frameIndex + B] = b; + _frames[frameIndex + A] = a; + _frames[frameIndex + R2] = r2; + _frames[frameIndex + G2] = g2; + _frames[frameIndex + B2] = b2; + } + + int TwoColorTimeline::getSlotIndex() { + return _slotIndex; + } + + void TwoColorTimeline::setSlotIndex(int inValue) { + assert(inValue >= 0); + _slotIndex = inValue; + } +} diff --git a/spine-cpp/spine-cpp/src/spine/Updatable.cpp b/spine-cpp/spine-cpp/src/spine/Updatable.cpp new file mode 100644 index 000000000..581df4b43 --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/Updatable.cpp @@ -0,0 +1,43 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +namespace Spine { + RTTI_IMPL_NOPARENT(Updatable); + + Updatable::Updatable() { + // Empty + } + + Updatable::~Updatable() { + // Empty + } +} diff --git a/spine-cpp/spine-cpp/src/spine/VertexAttachment.cpp b/spine-cpp/spine-cpp/src/spine/VertexAttachment.cpp new file mode 100644 index 000000000..be8cc1f1e --- /dev/null +++ b/spine-cpp/spine-cpp/src/spine/VertexAttachment.cpp @@ -0,0 +1,156 @@ +/****************************************************************************** +* Spine Runtimes Software License v2.5 +* +* Copyright (c) 2013-2016, Esoteric Software +* All rights reserved. +* +* You are granted a perpetual, non-exclusive, non-sublicensable, and +* non-transferable license to use, install, execute, and perform the Spine +* Runtimes software and derivative works solely for personal or internal +* use. Without the written permission of Esoteric Software (see Section 2 of +* the Spine Software License Agreement), you may not (a) modify, translate, +* adapt, or develop new applications using the Spine Runtimes or otherwise +* create derivative works or improvements of the Spine Runtimes or (b) remove, +* delete, alter, or obscure any trademarks or any copyright, trademark, patent, +* or other intellectual property or proprietary rights notices on or in the +* Software, including any copy thereof. Redistributions in binary or source +* form must include this license and terms. +* +* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include + +#include + +#include +#include + +namespace Spine { + RTTI_IMPL(VertexAttachment, Attachment); + + VertexAttachment::VertexAttachment(std::string name) : Attachment(name), _worldVerticesLength(0), _id(getNextID()) { + // Empty + } + + void VertexAttachment::computeWorldVertices(Slot& slot, Vector& worldVertices) { + computeWorldVertices(slot, 0, _worldVerticesLength, worldVertices, 0); + } + + void VertexAttachment::computeWorldVertices(Slot& slot, int start, int count, Vector& worldVertices, int offset, int stride) { + count = offset + (count >> 1) * stride; + Skeleton& skeleton = slot._bone._skeleton; + Vector& deformArray = slot.getAttachmentVertices(); + Vector& vertices = _vertices; + Vector& bones = _bones; + if (bones.size() == 0) { + if (deformArray.size() > 0) { + vertices = deformArray; + } + + Bone& bone = slot._bone; + float x = bone._worldX; + float y = bone._worldY; + float a = bone._a, b = bone._b, c = bone._c, d = bone._d; + for (int vv = start, w = offset; w < count; vv += 2, w += stride) { + float vx = vertices[vv]; + float vy = vertices[vv + 1]; + worldVertices[w] = vx * a + vy * b + x; + worldVertices[w + 1] = vx * c + vy * d + y; + } + return; + } + + int v = 0, skip = 0; + for (int i = 0; i < start; i += 2) { + int n = bones[v]; + v += n + 1; + skip += n; + } + + Vector& skeletonBones = skeleton.getBones(); + if (deformArray.size() == 0) { + for (int w = offset, b = skip * 3; w < count; w += stride) { + float wx = 0, wy = 0; + int n = bones[v++]; + n += v; + for (; v < n; v++, b += 3) { + Bone* boneP = skeletonBones[bones[v]]; + Bone& bone = *boneP; + float vx = vertices[b]; + float vy = vertices[b + 1]; + float weight = vertices[b + 2]; + wx += (vx * bone._a + vy * bone._b + bone._worldX) * weight; + wy += (vx * bone._c + vy * bone._d + bone._worldY) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } + else { + for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { + float wx = 0, wy = 0; + int n = bones[v++]; + n += v; + for (; v < n; v++, b += 3, f += 2) { + Bone* boneP = skeletonBones[bones[v]]; + Bone& bone = *boneP; + float vx = vertices[b] + deformArray[f]; + float vy = vertices[b + 1] + deformArray[f + 1]; + float weight = vertices[b + 2]; + wx += (vx * bone._a + vy * bone._b + bone._worldX) * weight; + wy += (vx * bone._c + vy * bone._d + bone._worldY) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } + } + + bool VertexAttachment::applyDeform(VertexAttachment* sourceAttachment) { + return this == sourceAttachment; + } + + int VertexAttachment::getId() { + return _id; + } + + Vector& VertexAttachment::getBones() { + return _bones; + } + + void VertexAttachment::setBones(Vector inValue) { + _bones = inValue; + } + + Vector& VertexAttachment::getVertices() { + return _vertices; + } + + void VertexAttachment::setVertices(Vector inValue) { + _vertices = inValue; + } + + int VertexAttachment::getWorldVerticesLength() { + return _worldVerticesLength; + } + + void VertexAttachment::setWorldVerticesLength(int inValue) { + _worldVerticesLength = inValue; + } + + int VertexAttachment::getNextID() { + static int nextID = 0; + + return (nextID++ & 65535) << 11; + } +}