You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

546 lines
20 KiB

/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "SkeletonCache.h"
#include "base/memory/Memory.h"
#include "spine-creator-support/AttachmentVertices.h"
USING_NS_MW; // NOLINT(google-build-using-namespace)
using namespace cc; // NOLINT(google-build-using-namespace)
namespace spine {
float SkeletonCache::FrameTime = 1.0F / 60.0F;
float SkeletonCache::MaxCacheTime = 120.0F;
SkeletonCache::SegmentData::SegmentData() = default;
SkeletonCache::SegmentData::~SegmentData() {
CC_SAFE_RELEASE_NULL(_texture);
}
void SkeletonCache::SegmentData::setTexture(cc::middleware::Texture2D *value) {
CC_SAFE_ADD_REF(value);
CC_SAFE_RELEASE(_texture);
_texture = value;
}
cc::middleware::Texture2D *SkeletonCache::SegmentData::getTexture() const {
return _texture;
}
SkeletonCache::FrameData::FrameData() = default;
SkeletonCache::FrameData::~FrameData() {
for (auto &bone : _bones) {
delete bone;
}
_bones.clear();
for (auto &color : _colors) {
delete color;
}
_colors.clear();
for (auto &segment : _segments) {
delete segment;
}
_segments.clear();
}
SkeletonCache::BoneData *SkeletonCache::FrameData::buildBoneData(std::size_t index) {
if (index > _bones.size()) return nullptr;
if (index == _bones.size()) {
auto *boneData = new BoneData;
_bones.push_back(boneData);
}
return _bones[index];
}
std::size_t SkeletonCache::FrameData::getBoneCount() const {
return _bones.size();
}
SkeletonCache::ColorData *SkeletonCache::FrameData::buildColorData(std::size_t index) {
if (index > _colors.size()) return nullptr;
if (index == _colors.size()) {
auto *colorData = new ColorData;
_colors.push_back(colorData);
}
return _colors[index];
}
std::size_t SkeletonCache::FrameData::getColorCount() const {
return _colors.size();
}
SkeletonCache::SegmentData *SkeletonCache::FrameData::buildSegmentData(std::size_t index) {
if (index > _segments.size()) return nullptr;
if (index == _segments.size()) {
auto *segmentData = new SegmentData;
_segments.push_back(segmentData);
}
return _segments[index];
}
std::size_t SkeletonCache::FrameData::getSegmentCount() const {
return _segments.size();
}
SkeletonCache::AnimationData::AnimationData() = default;
SkeletonCache::AnimationData::~AnimationData() {
reset();
}
void SkeletonCache::AnimationData::reset() {
for (auto &frame : _frames) {
delete frame;
}
_frames.clear();
_isComplete = false;
_totalTime = 0.0F;
}
bool SkeletonCache::AnimationData::needUpdate(int toFrameIdx) const {
return !_isComplete && _totalTime <= MaxCacheTime && (toFrameIdx == -1 || _frames.size() < toFrameIdx + 1);
}
SkeletonCache::FrameData *SkeletonCache::AnimationData::buildFrameData(std::size_t frameIdx) {
if (frameIdx > _frames.size()) {
return nullptr;
}
if (frameIdx == _frames.size()) {
auto *frameData = new FrameData();
_frames.push_back(frameData);
}
return _frames[frameIdx];
}
SkeletonCache::FrameData *SkeletonCache::AnimationData::getFrameData(std::size_t frameIdx) const {
if (frameIdx >= _frames.size()) {
return nullptr;
}
return _frames[frameIdx];
}
std::size_t SkeletonCache::AnimationData::getFrameCount() const {
return _frames.size();
}
SkeletonCache::SkeletonCache() = default;
SkeletonCache::~SkeletonCache() {
for (auto &animationCache : _animationCaches) {
delete animationCache.second;
}
_animationCaches.clear();
}
SkeletonCache::AnimationData *SkeletonCache::buildAnimationData(const std::string &animationName) {
AnimationData *aniData = nullptr;
auto it = _animationCaches.find(animationName);
if (it == _animationCaches.end()) {
auto *animation = findAnimation(animationName);
if (animation == nullptr) return nullptr;
aniData = new AnimationData();
aniData->_animationName = animationName;
_animationCaches[animationName] = aniData;
} else {
aniData = it->second;
}
return aniData;
}
SkeletonCache::AnimationData *SkeletonCache::getAnimationData(const std::string &animationName) {
auto it = _animationCaches.find(animationName);
if (it == _animationCaches.end()) {
return nullptr;
}
return it->second;
}
void SkeletonCache::update(float deltaTime) {
if (!_skeleton) return;
if (_ownsSkeleton) _skeleton->update(deltaTime);
_state->update(deltaTime);
_state->apply(*_skeleton);
_skeleton->updateWorldTransform();
}
void SkeletonCache::updateToFrame(const std::string &animationName, int toFrameIdx /*= -1*/) {
auto it = _animationCaches.find(animationName);
if (it == _animationCaches.end()) {
return;
}
AnimationData *animationData = it->second;
if (!animationData || !animationData->needUpdate(toFrameIdx)) {
return;
}
if (_curAnimationName != animationName) {
updateToFrame(_curAnimationName);
_curAnimationName = animationName;
}
// init animation
if (animationData->getFrameCount() == 0) {
setAnimation(0, animationName, false);
}
do {
update(FrameTime);
renderAnimationFrame(animationData);
animationData->_totalTime += FrameTime;
} while (animationData->needUpdate(toFrameIdx));
}
void SkeletonCache::renderAnimationFrame(AnimationData *animationData) {
std::size_t frameIndex = animationData->getFrameCount();
FrameData *frameData = animationData->buildFrameData(frameIndex);
if (!_skeleton) return;
// If opacity is 0,then return.
if (_skeleton->getColor().a == 0) {
return;
}
Color4F preColor(-1.0F, -1.0F, -1.0F, -1.0F);
Color4F preDarkColor(-1.0F, -1.0F, -1.0F, -1.0F);
// range [0.0, 1.0]
Color4F color;
Color4F darkColor;
Color4B finalColor;
Color4B finalDardk;
AttachmentVertices *attachmentVertices = nullptr;
middleware::IOBuffer &vb = frameData->vb;
middleware::IOBuffer &ib = frameData->ib;
// vertex size int bytes with two color
int vbs2 = sizeof(V3F_T2F_C4B_C4B);
// vertex size in floats with two color
int vs2 = static_cast<int32_t>(vbs2 / sizeof(float));
int vbSize = 0;
int ibSize = 0;
int preBlendMode = -1;
int preTextureIndex = -1;
int curTextureIndex = -1;
int preISegWritePos = -1;
int curISegLen = 0;
int curVSegLen = 0;
int materialLen = 0;
Slot *slot = nullptr;
middleware::Texture2D *texture = nullptr;
auto flush = [&]() {
// fill pre segment count field
if (preISegWritePos != -1) {
SegmentData *preSegmentData = frameData->buildSegmentData(materialLen - 1);
preSegmentData->indexCount = curISegLen;
preSegmentData->vertexFloatCount = curVSegLen;
}
SegmentData *segmentData = frameData->buildSegmentData(materialLen);
segmentData->setTexture(texture);
segmentData->blendMode = slot->getData().getBlendMode();
// save new segment count pos field
preISegWritePos = static_cast<int>(ib.getCurPos() / sizeof(uint16_t));
// reset pre blend mode to current
preBlendMode = static_cast<int>(slot->getData().getBlendMode());
// reset pre texture index to current
preTextureIndex = curTextureIndex;
// reset index segmentation count
curISegLen = 0;
// reset vertex segmentation count
curVSegLen = 0;
// material length increased
materialLen++;
};
auto &bones = _skeleton->getBones();
for (std::size_t i = 0, n = bones.size(); i < n; i++) {
auto &bone = bones[i];
auto boneCount = frameData->getBoneCount();
BoneData *boneData = frameData->buildBoneData(boneCount);
auto &matm = boneData->globalTransformMatrix.m;
matm[0] = bone->getA();
matm[1] = bone->getC();
matm[4] = bone->getB();
matm[5] = bone->getD();
matm[12] = bone->getWorldX();
matm[13] = bone->getWorldY();
}
auto &drawOrder = _skeleton->getDrawOrder();
for (size_t i = 0, n = drawOrder.size(); i < n; ++i) {
slot = drawOrder[i];
if (slot->getBone().isActive() == false) {
continue;
}
if (!slot->getAttachment()) {
_clipper->clipEnd(*slot);
continue;
}
const spine::Color &slotColor = slot->getColor();
TwoColorTriangles trianglesTwoColor;
spine::Color attachmentColor;
if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) {
auto *attachment = dynamic_cast<RegionAttachment *>(slot->getAttachment());
attachmentVertices = static_cast<AttachmentVertices *>(attachment->getRendererObject());
// Early exit if attachment is invisible
if (attachment->getColor().a == 0) {
_clipper->clipEnd(*slot);
continue;
}
trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount;
vbSize = static_cast<int32_t>(trianglesTwoColor.vertCount * sizeof(V3F_T2F_C4B_C4B));
vb.checkSpace(vbSize, true);
trianglesTwoColor.verts = reinterpret_cast<V3F_T2F_C4B_C4B *>(vb.getCurBuffer());
for (int ii = 0; ii < trianglesTwoColor.vertCount; ii++) {
trianglesTwoColor.verts[ii].texCoord = attachmentVertices->_triangles->verts[ii].texCoord;
}
attachment->computeWorldVertices(slot->getBone(), reinterpret_cast<float *>(trianglesTwoColor.verts), 0, vs2);
trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount;
ibSize = static_cast<int32_t>(trianglesTwoColor.indexCount * sizeof(uint16_t));
ib.checkSpace(ibSize, true);
trianglesTwoColor.indices = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
memcpy(trianglesTwoColor.indices, attachmentVertices->_triangles->indices, ibSize);
attachmentColor = attachment->getColor();
} else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) {
auto *attachment = dynamic_cast<MeshAttachment *>(slot->getAttachment());
attachmentVertices = static_cast<AttachmentVertices *>(attachment->getRendererObject());
// Early exit if attachment is invisible
if (attachment->getColor().a == 0) {
_clipper->clipEnd(*slot);
continue;
}
trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount;
vbSize = static_cast<int32_t>(trianglesTwoColor.vertCount * sizeof(V3F_T2F_C4B_C4B));
vb.checkSpace(vbSize, true);
trianglesTwoColor.verts = reinterpret_cast<V3F_T2F_C4B_C4B *>(vb.getCurBuffer());
for (int ii = 0; ii < trianglesTwoColor.vertCount; ii++) {
trianglesTwoColor.verts[ii].texCoord = attachmentVertices->_triangles->verts[ii].texCoord;
}
attachment->computeWorldVertices(*slot, 0, attachment->getWorldVerticesLength(), reinterpret_cast<float *>(trianglesTwoColor.verts), 0, vs2);
trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount;
ibSize = static_cast<int32_t>(trianglesTwoColor.indexCount * sizeof(uint16_t));
ib.checkSpace(ibSize, true);
trianglesTwoColor.indices = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
memcpy(trianglesTwoColor.indices, attachmentVertices->_triangles->indices, ibSize);
attachmentColor = attachment->getColor();
} else if (slot->getAttachment()->getRTTI().isExactly(ClippingAttachment::rtti)) {
auto *clip = dynamic_cast<ClippingAttachment *>(slot->getAttachment());
_clipper->clipStart(*slot, clip);
continue;
} else {
_clipper->clipEnd(*slot);
continue;
}
color.a = _skeleton->getColor().a * slotColor.a * attachmentColor.a * 255;
// skip rendering if the color of this attachment is 0
if (color.a == 0) {
_clipper->clipEnd(*slot);
continue;
}
float red = _skeleton->getColor().r * attachmentColor.r * 255;
float green = _skeleton->getColor().g * attachmentColor.g * 255;
float blue = _skeleton->getColor().b * attachmentColor.b * 255;
color.r = red * slotColor.r;
color.g = green * slotColor.g;
color.b = blue * slotColor.b;
if (slot->hasDarkColor()) {
darkColor.r = red * slot->getDarkColor().r;
darkColor.g = green * slot->getDarkColor().g;
darkColor.b = blue * slot->getDarkColor().b;
darkColor.a = 0;
} else {
darkColor.r = 0;
darkColor.g = 0;
darkColor.b = 0;
darkColor.a = 0;
}
finalColor.r = static_cast<uint8_t>(std::round(color.r));
finalColor.g = static_cast<uint8_t>(std::round(color.g));
finalColor.b = static_cast<uint8_t>(std::round(color.b));
finalColor.a = static_cast<uint8_t>(std::round(color.a));
finalDardk.r = static_cast<uint8_t>(std::round(darkColor.r));
finalDardk.g = static_cast<uint8_t>(std::round(darkColor.g));
finalDardk.b = static_cast<uint8_t>(std::round(darkColor.b));
finalDardk.a = static_cast<uint8_t>(std::round(darkColor.a));
if (preColor != color || preDarkColor != darkColor) {
preColor = color;
preDarkColor = darkColor;
auto colorCount = frameData->getColorCount();
if (colorCount > 0) {
ColorData *preColorData = frameData->buildColorData(colorCount - 1);
preColorData->vertexFloatOffset = static_cast<int>(vb.getCurPos() / sizeof(float));
}
ColorData *colorData = frameData->buildColorData(colorCount);
colorData->finalColor = finalColor;
colorData->darkColor = finalDardk;
}
// Two color tint logic
if (_clipper->isClipping()) {
_clipper->clipTriangles(reinterpret_cast<float *>(&trianglesTwoColor.verts[0].vertex), trianglesTwoColor.indices, trianglesTwoColor.indexCount, reinterpret_cast<float *>(&trianglesTwoColor.verts[0].texCoord), vs2);
if (_clipper->getClippedTriangles().size() == 0) {
_clipper->clipEnd(*slot);
continue;
}
trianglesTwoColor.vertCount = static_cast<int>(_clipper->getClippedVertices().size()) >> 1;
vbSize = static_cast<int32_t>(trianglesTwoColor.vertCount * sizeof(V3F_T2F_C4B_C4B));
vb.checkSpace(vbSize, true);
trianglesTwoColor.verts = reinterpret_cast<V3F_T2F_C4B_C4B *>(vb.getCurBuffer());
trianglesTwoColor.indexCount = static_cast<int>(_clipper->getClippedTriangles().size());
ibSize = static_cast<int32_t>(trianglesTwoColor.indexCount * sizeof(uint16_t));
ib.checkSpace(ibSize, true);
trianglesTwoColor.indices = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
memcpy(trianglesTwoColor.indices, _clipper->getClippedTriangles().buffer(), sizeof(uint16_t) * _clipper->getClippedTriangles().size());
float *verts = _clipper->getClippedVertices().buffer();
float *uvs = _clipper->getClippedUVs().buffer();
for (int v = 0, vn = trianglesTwoColor.vertCount, vv = 0; v < vn; ++v, vv += 2) {
V3F_T2F_C4B_C4B *vertex = trianglesTwoColor.verts + v;
vertex->vertex.x = verts[vv];
vertex->vertex.y = verts[vv + 1];
vertex->texCoord.u = uvs[vv];
vertex->texCoord.v = uvs[vv + 1];
vertex->color = finalColor;
vertex->color2 = finalDardk;
}
} else {
for (int v = 0, vn = trianglesTwoColor.vertCount; v < vn; ++v) {
V3F_T2F_C4B_C4B *vertex = trianglesTwoColor.verts + v;
vertex->color = finalColor;
vertex->color2 = finalDardk;
}
}
texture = attachmentVertices->_texture;
curTextureIndex = attachmentVertices->_texture->getRealTextureIndex();
// If texture or blendMode change,will change material.
if (preTextureIndex != curTextureIndex || preBlendMode != slot->getData().getBlendMode()) {
flush();
}
if (vbSize > 0 && ibSize > 0) {
auto vertexOffset = curVSegLen / vs2;
if (vertexOffset > 0) {
auto *ibBuffer = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
for (uint32_t ii = 0, nn = ibSize / sizeof(uint16_t); ii < nn; ii++) {
ibBuffer[ii] += vertexOffset;
}
}
vb.move(vbSize);
ib.move(ibSize);
// Record this turn index segmentation count,it will store in material buffer in the end.
curISegLen += static_cast<int32_t>(ibSize / sizeof(uint16_t));
curVSegLen += static_cast<int32_t>(vbSize / sizeof(float));
}
_clipper->clipEnd(*slot);
} // End slot traverse
_clipper->clipEnd();
if (preISegWritePos != -1) {
SegmentData *preSegmentData = frameData->buildSegmentData(materialLen - 1);
preSegmentData->indexCount = curISegLen;
preSegmentData->vertexFloatCount = curVSegLen;
}
auto colorCount = frameData->getColorCount();
if (colorCount > 0) {
ColorData *preColorData = frameData->buildColorData(colorCount - 1);
preColorData->vertexFloatOffset = static_cast<int>(vb.getCurPos() / sizeof(float));
}
}
void SkeletonCache::onAnimationStateEvent(TrackEntry *entry, EventType type, Event *event) {
SkeletonAnimation::onAnimationStateEvent(entry, type, event);
if (type == EventType_Complete && entry) {
Animation *ani = entry->getAnimation();
if (!ani) return;
std::string aniName = ani->getName().buffer();
if (aniName == _curAnimationName) {
AnimationData *aniData = getAnimationData(_curAnimationName);
if (!aniData) return;
aniData->_isComplete = true;
}
}
}
void SkeletonCache::resetAllAnimationData() {
for (auto &animationCache : _animationCaches) {
animationCache.second->reset();
}
}
void SkeletonCache::resetAnimationData(const std::string &animationName) {
for (auto &animationCache : _animationCaches) {
if (animationCache.second->_animationName == animationName) {
animationCache.second->reset();
break;
}
}
}
} // namespace spine