[cpp] Fixed up binary reader, added debug extension for memory profiling, fixed up RTTI usage, fixed update cache sorting.

This commit is contained in:
badlogic 2018-02-22 15:31:28 +01:00
parent 2d41846b1f
commit a8fd3e14be
26 changed files with 334 additions and 350 deletions

View File

@ -10,7 +10,6 @@ include_directories(../spine-cpp/include teamcity minicppunit tests memory)
set(SRC
src/main.cpp
src/TestHarness.cpp
)
add_executable(spine_cpp_unit_test ${SRC})

View File

@ -1,91 +0,0 @@
/******************************************************************************
* 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 "TestHarness.h"
void *Spine::TestSpineExtension::_alloc(size_t size, const char *file, int line) {
void* result = DefaultSpineExtension::_alloc(size, file, line);
_allocated.push_back(Allocation(result, size, file, line));
_allocations++;
return result;
}
void *Spine::TestSpineExtension::_calloc(size_t size, const char *file, int line) {
void* result = DefaultSpineExtension::_calloc(size, file, line);
_allocated.push_back(Allocation(result, size, file, line));
_allocations++;
return result;
}
void *Spine::TestSpineExtension::_realloc(void *ptr, size_t size, const char *file, int line) {
void* result = DefaultSpineExtension::_realloc(ptr, size, file, line);
_reallocations++;
for (std::vector<Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
if (it->address == ptr) {
it->address = result;
it->size = size;
it->fileName = file;
it->line = line;
return result;
}
}
_allocated.push_back(Allocation(result, size, file, line));
return result;
}
void Spine::TestSpineExtension::_free(void *mem, const char *file, int line) {
for (std::vector<Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
if (it->address == mem) {
DefaultSpineExtension::_free(mem, file, line);
_frees++;
_allocated.erase(it);
return;
}
}
printf("%s:%i (address %p): Double free or not allocated through SpineExtension\n", file, line, mem);
DefaultSpineExtension::_free(mem, file, line);
}
void Spine::TestSpineExtension::reportLeaks() {
for (std::vector<Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
printf("\"%s:%i (%zu bytes at %p)\n", it->fileName, it->line, it->size, it->address);
}
printf("allocations: %lu, reallocations: %lu, frees: %lu\n", _allocations, _reallocations, _frees);
if (_allocated.empty()) printf("No leaks detected");
}
void Spine::TestSpineExtension::clearAllocations() {
_allocated.resize(0);
}

View File

@ -30,8 +30,7 @@
#include <stdio.h>
#include <spine/spine.h>
#include "TestHarness.h"
#include <spine/Debug.h>
using namespace Spine;
@ -115,7 +114,7 @@ void testLoading() {
}
int main (int argc, char** argv) {
TestSpineExtension* ext = new TestSpineExtension();
DebugExtension* ext = new DebugExtension();
SpineExtension::setInstance(ext);
testLoading();

View File

@ -1,7 +1,32 @@
//
// Created by Mario Zechner on 2/20/18.
//
/******************************************************************************
* 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_COLOR_H
#define SPINE_COLOR_H

View File

@ -28,40 +28,87 @@
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef SPINE_TESTHARNESS_H
#define SPINE_TESTHARNESS_H
#ifndef SPINE_DEBUG_H
#define SPINE_DEBUG_H
#include <spine/Extension.h>
#include <vector>
namespace Spine {
struct Allocation {
void* address;
size_t size;
const char* fileName;
int line;
class DebugExtension: public DefaultSpineExtension {
struct Allocation {
void* address;
size_t size;
const char* fileName;
int line;
Allocation() : address(NULL), size(0), fileName(NULL), line(0) {
}
Allocation() : address(NULL), size(0), fileName(NULL), line(0) {
}
Allocation(void* a, size_t s, const char* f, int l) : address(a), size(s), fileName(f), line(l) {
}
};
Allocation(void* a, size_t s, const char* f, int l) : address(a), size(s), fileName(f), line(l) {
}
};
class TestSpineExtension: public DefaultSpineExtension {
public:
void reportLeaks ();
void clearAllocations();
void reportLeaks () {
for (std::vector<Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
printf("\"%s:%i (%zu bytes at %p)\n", it->fileName, it->line, it->size, it->address);
}
printf("allocations: %lu, reallocations: %lu, frees: %lu\n", _allocations, _reallocations, _frees);
if (_allocated.empty()) printf("No leaks detected");
}
void clearAllocations() {
_allocated.resize(0);
}
protected:
virtual void* _alloc(size_t size, const char* file, int line);
virtual void* _alloc(size_t size, const char* file, int line) {
void* result = DefaultSpineExtension::_alloc(size, file, line);
_allocated.push_back(Allocation(result, size, file, line));
_allocations++;
return result;
}
virtual void* _calloc(size_t size, const char* file, int line);
virtual void* _calloc(size_t size, const char* file, int line) {
void* result = DefaultSpineExtension::_calloc(size, file, line);
_allocated.push_back(Allocation(result, size, file, line));
_allocations++;
return result;
}
virtual void* _realloc(void* ptr, size_t size, const char* file, int line);
virtual void* _realloc(void* ptr, size_t size, const char* file, int line) {
void* result = DefaultSpineExtension::_realloc(ptr, size, file, line);
_reallocations++;
virtual void _free(void* mem, const char* file, int line);
for (std::vector<Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
if (it->address == ptr) {
it->address = result;
it->size = size;
it->fileName = file;
it->line = line;
return result;
}
}
_allocated.push_back(Allocation(result, size, file, line));
return result;
}
virtual void _free(void* mem, const char* file, int line) {
for (std::vector<Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
if (it->address == mem) {
DefaultSpineExtension::_free(mem, file, line);
_frees++;
_allocated.erase(it);
return;
}
}
printf("%s:%i (address %p): Double free or not allocated through SpineExtension\n", file, line, mem);
DefaultSpineExtension::_free(mem, file, line);
}
private:
std::vector<Allocation> _allocated;
@ -72,4 +119,4 @@ namespace Spine {
}
#endif //SPINE_TESTHARNESS_H
#endif //SPINE_DEBUG_H

View File

@ -99,6 +99,19 @@ namespace Spine {
virtual char* _readFile(const String& path, int* length);
};
struct Allocation {
void* address;
size_t size;
const char* fileName;
int line;
Allocation() : address(NULL), size(0), fileName(NULL), line(0) {
}
Allocation(void* a, size_t s, const char* f, int l) : address(a), size(s), fileName(f), line(l) {
}
};
}
#endif /* Spine_Extension_h */

View File

@ -45,8 +45,8 @@ namespace Spine {
const std::string& getClassName() const;
bool isExactly(const RTTI& rtti) const;
bool derivesFrom(const RTTI& rtti) const;
bool instanceOf(const RTTI &rtti) const;
private:
// Prevent copying

View File

@ -78,6 +78,8 @@ namespace Spine {
/// Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added
/// or removed.
void updateCache();
void printUpdateCache ();
/// Updates the world transform for each bone and applies constraints.
void updateWorldTransform();

View File

@ -113,9 +113,9 @@ namespace Spine {
int readVarint(DataInput* input, bool optimizePositive);
Skin* readSkin(DataInput* input, const char* skinName, SkeletonData* skeletonData, bool nonessential);
Skin* readSkin(DataInput* input, const String& skinName, SkeletonData* skeletonData, bool nonessential);
Attachment* readAttachment(DataInput* input, Skin* skin, int slotIndex, const char* attachmentName, SkeletonData* skeletonData, bool nonessential);
Attachment* readAttachment(DataInput* input, Skin* skin, int slotIndex, const String& attachmentName, SkeletonData* skeletonData, bool nonessential);
void readVertices(DataInput* input, VertexAttachment* attachment, int vertexCount);

View File

@ -61,7 +61,7 @@ namespace Spine {
public:
SlotData(int index, const String& name, BoneData& boneData);
const int getIndex();
int getIndex();
const String& getName();

View File

@ -72,6 +72,10 @@ namespace Spine {
return _length;
}
bool isEmpty () const {
return _length == 0;
}
const char* buffer () const {
return _buffer;
}

View File

@ -80,7 +80,7 @@ namespace Spine {
deallocate(_buffer);
}
void clear () {
inline void clear () {
for (size_t i = 0; i < _size; ++i) {
destroy(_buffer + (_size - 1 - i));
}
@ -88,11 +88,11 @@ namespace Spine {
_size = 0;
}
size_t size() const {
inline size_t size() const {
return _size;
}
void setSize(size_t newSize) {
inline void setSize(size_t newSize) {
assert(newSize >= 0);
_size = newSize;
if (_capacity < newSize) {
@ -102,13 +102,13 @@ namespace Spine {
}
}
void ensureCapacity(size_t newCapacity = 0) {
inline void ensureCapacity(size_t newCapacity = 0) {
if (_capacity >= newCapacity) return;
_capacity = newCapacity;
_buffer = SpineExtension::realloc<T>(_buffer, newCapacity, __FILE__, __LINE__);
}
void add(const T &inValue) {
inline void add(const T &inValue) {
if (_size == _capacity) {
_capacity = (int)(_size * 1.75f);
if (_capacity < 8) _capacity = 8;
@ -117,7 +117,7 @@ namespace Spine {
construct(_buffer + _size++, inValue);
}
void removeAt(size_t inIndex) {
inline void removeAt(size_t inIndex) {
assert(inIndex < _size);
--_size;
@ -131,7 +131,7 @@ namespace Spine {
destroy(_buffer + _size);
}
bool contains(const T& inValue) {
inline bool contains(const T& inValue) {
for (size_t i = 0; i < _size; ++i) {
if (_buffer[i] == inValue) {
return true;
@ -141,7 +141,7 @@ namespace Spine {
return false;
}
int indexOf(const T& inValue) {
inline int indexOf(const T& inValue) {
for (size_t i = 0; i < _size; ++i) {
if (_buffer[i] == inValue) {
return static_cast<int>(i);
@ -151,13 +151,13 @@ namespace Spine {
return -1;
}
T& operator[](size_t inIndex) {
inline T& operator[](size_t inIndex) {
assert(inIndex < _size);
return _buffer[inIndex];
}
friend bool operator==(Vector<T>& lhs, Vector<T>& rhs) {
inline friend bool operator==(Vector<T>& lhs, Vector<T>& rhs) {
if (lhs.size() != rhs.size()) {
return false;
}
@ -171,11 +171,11 @@ namespace Spine {
return true;
}
friend bool operator!=(Vector<T>& lhs, Vector<T>& rhs) {
inline friend bool operator!=(Vector<T>& lhs, Vector<T>& rhs) {
return !(lhs == rhs);
}
T* buffer() {
inline T* buffer() {
return _buffer;
}

View File

@ -428,7 +428,7 @@ namespace Spine {
MixPose pose = timelineData[ii] >= AnimationState::First ? MixPose_Setup : currentPose;
RotateTimeline* rotateTimeline = NULL;
if (timeline->getRTTI().derivesFrom(RotateTimeline::rtti)) {
if (timeline->getRTTI().isExactly(RotateTimeline::rtti)) {
rotateTimeline = static_cast<RotateTimeline*>(timeline);
}
@ -784,10 +784,10 @@ namespace Spine {
Timeline* timeline = timelines[i];
switch (timelineData[i]) {
case Subsequent:
if (!attachments && timeline->getRTTI().derivesFrom(AttachmentTimeline::rtti)) {
if (!attachments && timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) {
continue;
}
if (!drawOrder && timeline->getRTTI().derivesFrom(DrawOrderTimeline::rtti)) {
if (!drawOrder && timeline->getRTTI().isExactly(DrawOrderTimeline::rtti)) {
continue;
}
@ -811,7 +811,7 @@ namespace Spine {
from->_totalAlpha += alpha;
RotateTimeline* rotateTimeline = NULL;
if (timeline->getRTTI().derivesFrom(RotateTimeline::rtti)) {
if (timeline->getRTTI().isExactly(RotateTimeline::rtti)) {
rotateTimeline = static_cast<RotateTimeline*>(timeline);
}

View File

@ -59,7 +59,7 @@ namespace Spine {
Slot* slotP = skeleton._slots[_slotIndex];
Slot& slot = *slotP;
if (slot._attachment == NULL || !slot._attachment->getRTTI().derivesFrom(VertexAttachment::rtti)) {
if (slot._attachment == NULL || !slot._attachment->getRTTI().instanceOf(VertexAttachment::rtti)) {
return;
}

View File

@ -73,7 +73,7 @@ namespace Spine {
void PathConstraint::update() {
Attachment* baseAttachment = _target->getAttachment();
if (baseAttachment == NULL || !baseAttachment->getRTTI().derivesFrom(PathAttachment::rtti)) {
if (baseAttachment == NULL || !baseAttachment->getRTTI().instanceOf(PathAttachment::rtti)) {
return;
}

View File

@ -45,18 +45,18 @@ namespace Spine {
bool RTTI::isExactly(const RTTI& rtti) const {
return (this == &rtti);
}
bool RTTI::derivesFrom(const RTTI& rtti) const {
bool RTTI::instanceOf(const RTTI &rtti) const {
const RTTI * pCompare = this;
while (pCompare) {
if (pCompare == &rtti) {
return true;
}
pCompare = pCompare->_pBaseRTTI;
}
return false;
}
}

View File

@ -142,57 +142,57 @@ namespace Spine {
int pathCount = static_cast<int>(_pathConstraints.size());
int constraintCount = ikCount + transformCount + pathCount;
for (int i = 0; i < constraintCount; ++i) {
bool gotoNextConstraintCount = false;
int i = 0;
continue_outer:
for (; i < constraintCount; ++i) {
for (int ii = 0; ii < ikCount; ++ii) {
IkConstraint* constraint = _ikConstraints[ii];
if (constraint->getData().getOrder() == i) {
sortIkConstraint(constraint);
gotoNextConstraintCount = true;
break;
i++;
goto continue_outer;
}
}
if (gotoNextConstraintCount) {
break;
}
for (int ii = 0; ii < transformCount; ++ii) {
TransformConstraint* constraint = _transformConstraints[ii];
if (constraint->getData().getOrder() == i) {
sortTransformConstraint(constraint);
gotoNextConstraintCount = true;
break;
i++;
goto continue_outer;
}
}
if (gotoNextConstraintCount) {
break;
}
for (int ii = 0; ii < pathCount; ++ii) {
PathConstraint* constraint = _pathConstraints[ii];
if (constraint->getData().getOrder() == i) {
sortPathConstraint(constraint);
gotoNextConstraintCount = true;
break;
i++;
goto continue_outer;
}
}
if (gotoNextConstraintCount) {
break;
}
}
for (int i = 0, n = static_cast<int>(_bones.size()); i < n; ++i) {
sortBone(_bones[i]);
}
}
void Skeleton::printUpdateCache () {
for (size_t i = 0; i < _updateCache.size(); i++) {
Updatable* updatable = _updateCache[i];
if (updatable->getRTTI().isExactly(Bone::rtti)) {
printf("bone %s\n", ((Bone*)updatable)->getData().getName().buffer());
} else if (updatable->getRTTI().isExactly(TransformConstraint::rtti)) {
printf("transform constraint %s\n", ((TransformConstraint*)updatable)->getData().getName().buffer());
} else if (updatable->getRTTI().isExactly(IkConstraint::rtti)) {
printf("ik constraint %s\n", ((IkConstraint*)updatable)->getData().getName().buffer());
} else if (updatable->getRTTI().isExactly(PathConstraint::rtti)) {
printf("path constraint %s\n", ((PathConstraint*)updatable)->getData().getName().buffer());
}
}
}
void Skeleton::updateWorldTransform() {
for (int i = 0, n = static_cast<int>(_updateCacheReset.size()); i < n; ++i) {
@ -407,7 +407,7 @@ namespace Spine {
int verticesLength = 0;
Attachment* attachment = slot->getAttachment();
if (attachment != NULL && attachment->getRTTI().derivesFrom(RegionAttachment::rtti)) {
if (attachment != NULL && attachment->getRTTI().instanceOf(RegionAttachment::rtti)) {
RegionAttachment* regionAttachment = static_cast<RegionAttachment*>(attachment);
verticesLength = 8;
@ -416,7 +416,7 @@ namespace Spine {
}
regionAttachment->computeWorldVertices(slot->getBone(), outVertexBuffer, 0);
}
else if (attachment != NULL && attachment->getRTTI().derivesFrom(MeshAttachment::rtti)) {
else if (attachment != NULL && attachment->getRTTI().instanceOf(MeshAttachment::rtti)) {
MeshAttachment* mesh = static_cast<MeshAttachment*>(attachment);
verticesLength = mesh->getWorldVerticesLength();
@ -543,9 +543,7 @@ namespace Spine {
if (constrained.size() > 1) {
Bone* child = constrained[constrained.size() - 1];
if (!_updateCache.contains(child)) {
_updateCacheReset.add(child);
}
if (!_updateCache.contains(child)) _updateCacheReset.add(child);
}
_updateCache.add(constraint);
@ -556,72 +554,55 @@ namespace Spine {
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) {
int slotIndex = slot->getData().getIndex();
Bone& slotBone = slot->getBone();
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<int>(_data->_skins.size()); ii < nn; ++ii) {
for (size_t ii = 0, nn = _data->_skins.size(); ii < nn; ii++)
sortPathConstraintAttachment(_data->_skins[ii], slotIndex, slotBone);
}
Attachment* attachment = slot->_attachment;
if (attachment != NULL && attachment->getRTTI().derivesFrom(PathAttachment::rtti)) {
if (attachment != NULL && attachment->getRTTI().instanceOf(PathAttachment::rtti))
sortPathConstraintAttachment(attachment, slotBone);
}
Vector<Bone*>& constrained = constraint->getBones();
int boneCount = static_cast<int>(constrained.size());
for (int i = 0; i < boneCount; ++i) {
size_t boneCount = constrained.size();
for (size_t i = 0; i < boneCount; ++i) {
sortBone(constrained[i]);
}
_updateCache.add(constraint);
for (int i = 0; i < boneCount; ++i) {
for (int i = 0; i < boneCount; i++)
sortReset(constrained[i]->getChildren());
}
for (int i = 0; i < boneCount; ++i) {
for (int i = 0; i < boneCount; i++)
constrained[i]->_sorted = true;
}
}
void Skeleton::sortTransformConstraint(TransformConstraint* constraint) {
sortBone(constraint->getTarget());
Vector<Bone*>& constrained = constraint->getBones();
int boneCount = static_cast<int>(constrained.size());
size_t boneCount = constrained.size();
if (constraint->_data.isLocal()) {
for (int i = 0; i < boneCount; ++i) {
for (size_t i = 0; i < boneCount; i++) {
Bone* child = constrained[i];
sortBone(child->getParent());
if (!_updateCache.contains(child)) {
_updateCacheReset.add(child);
}
if (!_updateCache.contains(child)) _updateCacheReset.add(child);
}
}
else {
for (int i = 0; i < boneCount; ++i) {
} else {
for (size_t i = 0; i < boneCount; ++i) {
sortBone(constrained[i]);
}
}
_updateCache.add(constraint);
for (int i = 0; i < boneCount; ++i) {
for (size_t i = 0; i < boneCount; ++i)
sortReset(constrained[i]->getChildren());
}
for (int i = 0; i < boneCount; ++i) {
for (size_t i = 0; i < boneCount; ++i)
constrained[i]->_sorted = true;
}
}
void Skeleton::sortPathConstraintAttachment(Skin* skin, int slotIndex, Bone& slotBone) {
@ -638,18 +619,12 @@ namespace Spine {
}
void Skeleton::sortPathConstraintAttachment(Attachment* attachment, Bone& slotBone) {
if (attachment == NULL || attachment->getRTTI().derivesFrom(PathAttachment::rtti)) {
return;
}
PathAttachment* pathAttachment = static_cast<PathAttachment*>(attachment);
Vector<int>& pathBonesRef = pathAttachment->getBones();
Vector<int> pathBones = pathBonesRef;
if (pathBones.size() == 0) {
if (attachment == NULL || !attachment->getRTTI().instanceOf(PathAttachment::rtti)) return;
Vector<int>& pathBones = static_cast<PathAttachment*>(attachment)->getBones();
if (pathBones.size() == 0)
sortBone(&slotBone);
}
else {
for (int i = 0, n = static_cast<int>(pathBones.size()); i < n;) {
for (size_t i = 0, n = pathBones.size(); i < n;) {
int nn = pathBones[i++];
nn += i;
while (i < nn) {
@ -660,29 +635,17 @@ namespace Spine {
}
void Skeleton::sortBone(Bone* bone) {
assert(bone != NULL);
if (bone->_sorted) {
return;
}
if (bone->_sorted) return;
Bone* parent = bone->_parent;
if (parent != NULL) {
sortBone(parent);
}
if (parent != NULL) sortBone(parent);
bone->_sorted = true;
_updateCache.add(bone);
}
void Skeleton::sortReset(Vector<Bone*>& bones) {
for (size_t i = 0; i < bones.size(); ++i) {
for (size_t i = 0, n = bones.size(); i < n; ++i) {
Bone* bone = bones[i];
if (bone->_sorted) {
sortReset(bone->getChildren());
}
if (bone->_sorted) sortReset(bone->getChildren());
bone->_sorted = false;
}
}

View File

@ -492,7 +492,7 @@ namespace Spine {
return value;
}
Skin* SkeletonBinary::readSkin(DataInput* input, const char* skinName, SkeletonData* skeletonData, bool nonessential) {
Skin* SkeletonBinary::readSkin(DataInput* input, const String& skinName, SkeletonData* skeletonData, bool nonessential) {
Skin* skin = NULL;
int slotCount = readVarint(input, true);
int i, ii, nn;
@ -500,7 +500,7 @@ namespace Spine {
return NULL;
}
skin = new (__FILE__, __LINE__) Skin(String(skinName));
skin = new (__FILE__, __LINE__) Skin(skinName);
for (i = 0; i < slotCount; ++i) {
int slotIndex = readVarint(input, true);
@ -517,27 +517,17 @@ namespace Spine {
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<AttachmentType>(readByte(input));
Attachment* SkeletonBinary::readAttachment(DataInput* input, Skin* skin, int slotIndex, const String& attachmentName, SkeletonData* skeletonData, bool nonessential) {
String name(readString(input), true);
if (name.isEmpty()) name = attachmentName;
AttachmentType type = static_cast<AttachmentType>(readByte(input));
switch (type) {
case AttachmentType_Region: {
const char* path = readString(input);
RegionAttachment* region;
if (!path) {
path = name;
}
region = _attachmentLoader->newRegionAttachment(*skin, String(name), String(path));
region->_path = String(path);
String path(readString(input), true);
if (path.isEmpty()) path = name;
RegionAttachment *region = _attachmentLoader->newRegionAttachment(*skin, String(name), String(path));
region->_path = path;
region->_rotation = readFloat(input);
region->_x = readFloat(input) * _scale;
region->_y = readFloat(input) * _scale;
@ -547,68 +537,50 @@ namespace Spine {
region->_height = readFloat(input) * _scale;
readColor(input, region->getColor());
region->updateOffset();
if (freeName) {
SpineExtension::free(name, __FILE__, __LINE__);
}
return region;
}
case AttachmentType_Boundingbox: {
int vertexCount = readVarint(input, true);
BoundingBoxAttachment* box = _attachmentLoader->newBoundingBoxAttachment(*skin, String(name));
readVertices(input, static_cast<VertexAttachment*>(box), vertexCount);
BoundingBoxAttachment *box = _attachmentLoader->newBoundingBoxAttachment(*skin, String(name));
readVertices(input, static_cast<VertexAttachment *>(box), vertexCount);
if (nonessential) {
/* Skip color. */
readInt(input);
}
if (freeName) {
SpineExtension::free(name, __FILE__, __LINE__);
}
return box;
}
case AttachmentType_Mesh: {
int vertexCount;
MeshAttachment* mesh;
const char* path = readString(input);
if (!path) {
path = name;
}
MeshAttachment *mesh;
String path(readString(input), true);
if (path.isEmpty()) path = name;
mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path));
mesh->_path = String(path);
readColor(input, mesh->getColor());
vertexCount = readVarint(input, true);
readFloatArray(input, vertexCount << 1, 1, mesh->getRegionUVs());
readShortArray(input, mesh->getTriangles());
readVertices(input, static_cast<VertexAttachment*>(mesh), vertexCount);
readVertices(input, static_cast<VertexAttachment *>(mesh), vertexCount);
mesh->updateUVs();
mesh->_hullLength = readVarint(input, true) << 1;
if (nonessential) {
readShortArray(input, mesh->getEdges());
mesh->_width = readFloat(input) * _scale;
mesh->_height = readFloat(input) * _scale;
}
else {
} else {
mesh->_width = 0;
mesh->_height = 0;
}
if (freeName) {
SpineExtension::free(name, __FILE__, __LINE__);
}
return mesh;
}
case AttachmentType_Linkedmesh: {
const char* skinName;
const char* parent;
MeshAttachment* mesh;
const char* path = readString(input);
if (!path) {
path = name;
}
const char *skinName;
const char *parent;
MeshAttachment *mesh;
String path(readString(input), true);
if (path.isEmpty()) path = name;
mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path));
mesh->_path = path;
readColor(input, mesh->getColor());
@ -619,110 +591,91 @@ namespace Spine {
mesh->_width = readFloat(input) * _scale;
mesh->_height = readFloat(input) * _scale;
}
LinkedMesh* linkedMesh = new (__FILE__, __LINE__) LinkedMesh(mesh, String(skinName), slotIndex, String(parent));
_linkedMeshes.add(linkedMesh);
if (freeName) {
SpineExtension::free(name, __FILE__, __LINE__);
}
LinkedMesh *linkedMesh = new(__FILE__, __LINE__) LinkedMesh(mesh, String(skinName), slotIndex,
String(parent));
_linkedMeshes.add(linkedMesh);
SpineExtension::free(skinName, __FILE__, __LINE__);
SpineExtension::free(parent, __FILE__, __LINE__);
return mesh;
}
case AttachmentType_Path: {
PathAttachment* path = _attachmentLoader->newPathAttachment(*skin, String(name));
PathAttachment *path = _attachmentLoader->newPathAttachment(*skin, String(name));
int vertexCount = 0;
path->_closed = readBoolean(input);
path->_constantSpeed = readBoolean(input);
vertexCount = readVarint(input, true);
readVertices(input, static_cast<VertexAttachment*>(path), vertexCount);
readVertices(input, static_cast<VertexAttachment *>(path), vertexCount);
int lengthsLength = vertexCount / 3;
path->_lengths.setSize(lengthsLength);
for (i = 0; i < lengthsLength; ++i) {
for (int i = 0; i < lengthsLength; ++i) {
path->_lengths[i] = readFloat(input) * _scale;
}
if (nonessential) {
/* Skip color. */
readInt(input);
}
if (freeName) {
SpineExtension::free(name, __FILE__, __LINE__);
}
return path;
}
case AttachmentType_Point: {
PointAttachment* point = _attachmentLoader->newPointAttachment(*skin, String(name));
PointAttachment *point = _attachmentLoader->newPointAttachment(*skin, 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, true);
int vertexCount = readVarint(input, true);
ClippingAttachment* clip = _attachmentLoader->newClippingAttachment(*skin, name);
readVertices(input, static_cast<VertexAttachment*>(clip), vertexCount);
ClippingAttachment *clip = _attachmentLoader->newClippingAttachment(*skin, name);
readVertices(input, static_cast<VertexAttachment *>(clip), vertexCount);
if (nonessential) {
/* Skip color. */
readInt(input);
}
clip->_endSlot = skeletonData->_slots[endSlotIndex];
if (freeName) {
SpineExtension::free(name, __FILE__, __LINE__);
}
return clip;
}
}
if (freeName) {
SpineExtension::free(name, __FILE__, __LINE__);
}
return NULL;
}
void SkeletonBinary::readVertices(DataInput* input, VertexAttachment* attachment, int vertexCount) {
float scale = _scale;
int verticesLength = vertexCount << 1;
attachment->setWorldVerticesLength(vertexCount << 1);
if (!readBoolean(input)) {
readFloatArray(input, verticesLength, scale, attachment->getVertices());
return;
}
Vertices vertices;
vertices._bones.ensureCapacity(verticesLength * 3);
vertices._vertices.ensureCapacity(verticesLength * 3 * 3);
Vector<float>& vertices = attachment->getVertices();
Vector<int>& bones = attachment->getBones();
vertices.ensureCapacity(verticesLength * 3 * 3);
bones.ensureCapacity(verticesLength * 3);
for (int i = 0; i < vertexCount; ++i) {
int boneCount = readVarint(input, true);
vertices._bones.add(boneCount);
bones.add(boneCount);
for (int ii = 0; ii < boneCount; ++ii) {
vertices._bones.add(readVarint(input, true));
vertices._vertices.add(readFloat(input) * scale);
vertices._vertices.add(readFloat(input) * scale);
vertices._vertices.add(readFloat(input));
bones.add(readVarint(input, true));
vertices.add(readFloat(input) * scale);
vertices.add(readFloat(input) * scale);
vertices.add(readFloat(input));
}
}
attachment->setVertices(vertices._vertices);
attachment->setBones(vertices._bones);
}
void SkeletonBinary::readFloatArray(DataInput *input, int n, float scale, Vector<float>& array) {

View File

@ -54,7 +54,7 @@ namespace Spine {
for (int i = 0; i < slotCount; i++) {
Slot* slot = slots[i];
Attachment* attachment = slot->_attachment;
if (attachment == NULL || !attachment->getRTTI().derivesFrom(BoundingBoxAttachment::rtti)) {
if (attachment == NULL || !attachment->getRTTI().instanceOf(BoundingBoxAttachment::rtti)) {
continue;
}
BoundingBoxAttachment* boundingBox = static_cast<BoundingBoxAttachment*>(attachment);

View File

@ -49,7 +49,6 @@ namespace Spine {
std::size_t Skin::HashAttachmentKey::operator()(const Spine::Skin::AttachmentKey& val) const {
std::size_t h1 = val._slotIndex;
return h1;
}

View File

@ -46,7 +46,7 @@ namespace Spine {
assert(_name.length() > 0);
}
const int SlotData::getIndex() {
int SlotData::getIndex() {
return _index;
}

View File

@ -208,6 +208,7 @@ public class SkeletonViewer extends ApplicationAdapter {
}
skeleton = new Skeleton(skeletonData);
skeleton.updateWorldTransform();
skeleton.setToSetupPose();
skeleton = new Skeleton(skeleton); // Tests copy constructors.
skeleton.updateWorldTransform();

View File

@ -1,5 +1,49 @@
cmake_minimum_required(VERSION 2.8.9)
#
# First download and extract SFML 2.3.2 for the respective OS we are on
#
set(DEPS_DIR "${CMAKE_CURRENT_LIST_DIR}/dependencies/")
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(SFML_URL "http://www.sfml-dev.org/files/SFML-2.4.1-osx-clang.tar.gz")
set(SFML_DIR ${DEPS_DIR}/SFML-2.4.1-osx-clang)
if (NOT EXISTS "${SFML_DIR}")
message("Downloading SFML for Mac OS X")
file(DOWNLOAD "${SFML_URL}" "${DEPS_DIR}/sfml.tar.gz")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf ${DEPS_DIR}/sfml.tar.gz
WORKING_DIRECTORY ${DEPS_DIR}
)
# copy freetype over to Frameworks/ so rpath resoultion works
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_directory ${SFML_DIR}/extlibs/freetype.framework ${SFML_DIR}/Frameworks/freetype.framework
WORKING_DIRECTORY ${SFML_DIR}
)
endif()
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(SFML_URL "http://www.sfml-dev.org/files/SFML-2.4.1-linux-gcc-64-bit.tar.gz")
set(SFML_DIR ${DEPS_DIR}/SFML-2.4.1)
if (NOT EXISTS ${SFML_DIR})
message("Downloading SFML for Linux 64-bit")
file(DOWNLOAD "${SFML_URL}" "${DEPS_DIR}/sfml.tar.gz")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf ${DEPS_DIR}/sfml.tar.gz
WORKING_DIRECTORY ${DEPS_DIR}
)
endif()
else()
set(SFML_URL "http://www.sfml-dev.org/files/SFML-2.4.1-windows-vc14-32-bit.zip")
set(SFML_DIR ${DEPS_DIR}/SFML-2.4.1)
if (NOT EXISTS ${SFML_DIR})
message("Downloading SFML for Windows 32-bit")
file(DOWNLOAD "${SFML_URL}" "${DEPS_DIR}/sfml.zip")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar x ${DEPS_DIR}/sfml.zip
WORKING_DIRECTORY ${DEPS_DIR}
)
endif()
endif()
# Define spine-sfml library
include_directories(src ${SFML_DIR}/include)
file(GLOB INCLUDES "src/**/*.h")

View File

@ -30,8 +30,8 @@
#include <iostream>
#include <string.h>
#define SPINE_SHORT_NAMES
#include <spine/spine-sfml.h>
#include <spine/Debug.h>
#include <SFML/Graphics.hpp>
#include <SFML/Window/Mouse.hpp>
@ -94,7 +94,8 @@ SkeletonData* readSkeletonBinaryData (const char* filename, Atlas* atlas, float
void testcase (void func(SkeletonData* skeletonData, Atlas* atlas),
const char* jsonName, const char* binaryName, const char* atlasName,
float scale) {
Atlas* atlas = new (__FILE__, __LINE__) Atlas(atlasName, 0);
SFMLTextureLoader textureLoader;
Atlas* atlas = new (__FILE__, __LINE__) Atlas(atlasName, &textureLoader);
SkeletonData* skeletonData = readSkeletonJsonData(jsonName, atlas, scale);
func(skeletonData, atlas);
@ -440,15 +441,17 @@ void test (SkeletonData* skeletonData, Atlas* atlas) {
}
int main () {
DebugExtension dbgExtension;
SpineExtension::setInstance(&dbgExtension);
testcase(test, "data/tank-pro.json", "data/tank-pro.skel", "data/tank.atlas", 1.0f);
testcase(spineboy, "data/spineboy-ess.json", "data/spineboy-ess.skel", "data/spineboy.atlas", 0.6f);
/*testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl.atlas", 0.5f);
testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl.atlas", 0.5f);
testcase(coin, "data/coin-pro.json", "data/coin-pro.skel", "data/coin.atlas", 0.5f);
testcase(vine, "data/vine-pro.json", "data/vine-pro.skel", "data/vine.atlas", 0.5f);
testcase(tank, "data/tank-pro.json", "data/tank-pro.skel", "data/tank.atlas", 0.2f);
testcase(raptor, "data/raptor-pro.json", "data/raptor-pro.skel", "data/raptor.atlas", 0.5f);
testcase(spineboy, "data/spineboy-ess.json", "data/spineboy-ess.skel", "data/spineboy.atlas", 0.6f);
testcase(goblins, "data/goblins-pro.json", "data/goblins-pro.skel", "data/goblins.atlas", 1.4f);
testcase(stretchyman, "data/stretchyman-pro.json", "data/stretchyman-pro.skel", "data/stretchyman.atlas", 0.6f);*/
testcase(stretchyman, "data/stretchyman-pro.json", "data/stretchyman-pro.skel", "data/stretchyman.atlas", 0.6f);
dbgExtension.reportLeaks();
return 0;
}

View File

@ -130,7 +130,7 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
int indicesCount = 0;
Color* attachmentColor;
if (attachment->rtti.derivesFrom(RegionAttachment::rtti)) {
if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) {
RegionAttachment* regionAttachment = (RegionAttachment*)attachment;
regionAttachment->computeWorldVertices(slot.getBone(), worldVertices, 0, 2);
verticesCount = 4;
@ -140,7 +140,7 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
texture = (Texture*)((AtlasRegion*)regionAttachment->getRendererObject())->page->rendererObject;
attachmentColor = &regionAttachment->getColor();
} else if (attachment->rtti.derivesFrom(MeshAttachment::rtti)) {
} else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
MeshAttachment* mesh = (MeshAttachment*)attachment;
if (mesh->getWorldVerticesLength() > worldVertices.size()) worldVertices.setSize(mesh->getWorldVerticesLength());
texture = (Texture*)((AtlasRegion*)mesh->getRendererObject())->page->rendererObject;
@ -150,7 +150,7 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
indices = &mesh->getTriangles();
indicesCount = mesh->getTriangles().size();
attachmentColor = &mesh->getColor();
} else if (attachment->rtti.derivesFrom(ClippingAttachment::rtti)) {
} else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) {
ClippingAttachment* clip = (ClippingAttachment*)slot.getAttachment();
clipper.clipStart(slot, clip);
continue;
@ -280,4 +280,20 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
// BOZO if (vertexEffect != 0) vertexEffect->end(vertexEffect);
}
void SFMLTextureLoader::load(AtlasPage &page, const String &path) {
Texture* texture = new Texture();
if (!texture->loadFromFile(path.buffer())) return;
if (page.magFilter == TextureFilter_Linear) texture->setSmooth(true);
if (page.uWrap == TextureWrap_Repeat && page.vWrap == TextureWrap_Repeat) texture->setRepeated(true);
page.rendererObject = texture;
Vector2u size = texture->getSize();
page.width = size.x;
page.height = size.y;
}
void SFMLTextureLoader::unload(void *texture) {
delete (Texture*)texture;
}
} /* namespace spine */

View File

@ -66,5 +66,12 @@ private:
mutable bool usePremultipliedAlpha;
};
class SFMLTextureLoader: public TextureLoader {
public:
virtual void load(AtlasPage& page, const String& path);
virtual void unload(void* texture);
};
} /* namespace spine */
#endif /* SPINE_SFML_H_ */