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.
419 lines
13 KiB
419 lines
13 KiB
/**
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2012-2020 DragonBones team and other contributors
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "ArmatureCache.h"
|
|
#include "CCFactory.h"
|
|
#include "base/TypeDef.h"
|
|
#include "base/memory/Memory.h"
|
|
|
|
USING_NS_MW; // NOLINT(google-build-using-namespace)
|
|
|
|
using namespace cc; // NOLINT(google-build-using-namespace)
|
|
|
|
DRAGONBONES_NAMESPACE_BEGIN
|
|
|
|
float ArmatureCache::FrameTime = 1.0F / 60.0F;
|
|
float ArmatureCache::MaxCacheTime = 120.0F;
|
|
|
|
ArmatureCache::SegmentData::SegmentData() = default;
|
|
|
|
ArmatureCache::SegmentData::~SegmentData() {
|
|
CC_SAFE_RELEASE_NULL(_texture);
|
|
}
|
|
|
|
void ArmatureCache::SegmentData::setTexture(cc::middleware::Texture2D *value) {
|
|
CC_SAFE_ADD_REF(value);
|
|
CC_SAFE_RELEASE(_texture);
|
|
_texture = value;
|
|
}
|
|
|
|
cc::middleware::Texture2D *ArmatureCache::SegmentData::getTexture() const {
|
|
return _texture;
|
|
}
|
|
|
|
ArmatureCache::FrameData::FrameData() = default;
|
|
|
|
ArmatureCache::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();
|
|
}
|
|
|
|
ArmatureCache::BoneData *ArmatureCache::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 ArmatureCache::FrameData::getBoneCount() const {
|
|
return _bones.size();
|
|
}
|
|
|
|
ArmatureCache::ColorData *ArmatureCache::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 ArmatureCache::FrameData::getColorCount() const {
|
|
return _colors.size();
|
|
}
|
|
|
|
ArmatureCache::SegmentData *ArmatureCache::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 ArmatureCache::FrameData::getSegmentCount() const {
|
|
return _segments.size();
|
|
}
|
|
|
|
ArmatureCache::AnimationData::AnimationData() = default;
|
|
|
|
ArmatureCache::AnimationData::~AnimationData() {
|
|
reset();
|
|
}
|
|
|
|
void ArmatureCache::AnimationData::reset() {
|
|
for (auto &frame : _frames) {
|
|
delete frame;
|
|
}
|
|
_frames.clear();
|
|
_isComplete = false;
|
|
_totalTime = 0.0F;
|
|
}
|
|
|
|
bool ArmatureCache::AnimationData::needUpdate(int toFrameIdx) const {
|
|
return !_isComplete && _totalTime <= MaxCacheTime && (toFrameIdx == -1 || _frames.size() < toFrameIdx + 1);
|
|
}
|
|
|
|
ArmatureCache::FrameData *ArmatureCache::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];
|
|
}
|
|
|
|
ArmatureCache::FrameData *ArmatureCache::AnimationData::getFrameData(std::size_t frameIdx) const {
|
|
if (frameIdx >= _frames.size()) {
|
|
return nullptr;
|
|
}
|
|
return _frames[frameIdx];
|
|
}
|
|
|
|
std::size_t ArmatureCache::AnimationData::getFrameCount() const {
|
|
return _frames.size();
|
|
}
|
|
|
|
ArmatureCache::ArmatureCache(const std::string &armatureName, const std::string &armatureKey, const std::string &atlasUUID) {
|
|
_armatureDisplay = dragonBones::CCFactory::getFactory()->buildArmatureDisplay(armatureName, armatureKey, "", atlasUUID);
|
|
if (_armatureDisplay) {
|
|
_armatureDisplay->addRef();
|
|
}
|
|
}
|
|
|
|
ArmatureCache::~ArmatureCache() {
|
|
if (_armatureDisplay) {
|
|
_armatureDisplay->release();
|
|
_armatureDisplay = nullptr;
|
|
}
|
|
|
|
for (auto &animationCache : _animationCaches) {
|
|
delete animationCache.second;
|
|
}
|
|
_animationCaches.clear();
|
|
}
|
|
|
|
ArmatureCache::AnimationData *ArmatureCache::buildAnimationData(const std::string &animationName) {
|
|
if (!_armatureDisplay) return nullptr;
|
|
|
|
AnimationData *aniData = nullptr;
|
|
auto it = _animationCaches.find(animationName);
|
|
if (it == _animationCaches.end()) {
|
|
auto *armature = _armatureDisplay->getArmature();
|
|
auto *animation = armature->getAnimation();
|
|
auto hasAni = animation->hasAnimation(animationName);
|
|
if (!hasAni) return nullptr;
|
|
|
|
aniData = new AnimationData();
|
|
aniData->_animationName = animationName;
|
|
_animationCaches[animationName] = aniData;
|
|
} else {
|
|
aniData = it->second;
|
|
}
|
|
return aniData;
|
|
}
|
|
|
|
ArmatureCache::AnimationData *ArmatureCache::getAnimationData(const std::string &animationName) {
|
|
auto it = _animationCaches.find(animationName);
|
|
if (it == _animationCaches.end()) {
|
|
return nullptr;
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
void ArmatureCache::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;
|
|
}
|
|
|
|
auto *armature = _armatureDisplay->getArmature();
|
|
auto *animation = armature->getAnimation();
|
|
|
|
// init animation
|
|
if (animationData->getFrameCount() == 0) {
|
|
animation->play(animationName, 1);
|
|
}
|
|
|
|
do {
|
|
armature->advanceTime(FrameTime);
|
|
renderAnimationFrame(animationData);
|
|
animationData->_totalTime += FrameTime;
|
|
if (animation->isCompleted()) {
|
|
animationData->_isComplete = true;
|
|
}
|
|
} while (animationData->needUpdate(toFrameIdx));
|
|
}
|
|
|
|
void ArmatureCache::renderAnimationFrame(AnimationData *animationData) {
|
|
std::size_t frameIndex = animationData->getFrameCount();
|
|
_frameData = animationData->buildFrameData(frameIndex);
|
|
|
|
_preColor = Color4B(0, 0, 0, 0);
|
|
_color = Color4B(255, 255, 255, 255);
|
|
|
|
_preBlendMode = -1;
|
|
_preTextureIndex = -1;
|
|
_curTextureIndex = -1;
|
|
_preISegWritePos = -1;
|
|
_curISegLen = 0;
|
|
_curVSegLen = 0;
|
|
_materialLen = 0;
|
|
|
|
auto *armature = _armatureDisplay->getArmature();
|
|
traverseArmature(armature);
|
|
|
|
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>(_frameData->vb.getCurPos()) / sizeof(float);
|
|
}
|
|
|
|
_frameData = nullptr;
|
|
}
|
|
|
|
void ArmatureCache::traverseArmature(Armature *armature, float parentOpacity /*= 1.0f*/) {
|
|
middleware::IOBuffer &vb = _frameData->vb;
|
|
middleware::IOBuffer &ib = _frameData->ib;
|
|
|
|
const auto &bones = armature->getBones();
|
|
Bone *bone = nullptr;
|
|
const auto &slots = armature->getSlots();
|
|
CCSlot *slot = nullptr;
|
|
// range [0.0, 1.0]
|
|
Color4B preColor(0, 0, 0, 0);
|
|
Color4B color;
|
|
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 = static_cast<int>(slot->_blendMode);
|
|
|
|
// 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->_blendMode);
|
|
// reset pre texture index to current
|
|
_preTextureIndex = _curTextureIndex;
|
|
// reset index segmentation count
|
|
_curISegLen = 0;
|
|
// reset vertex segmentation count
|
|
_curVSegLen = 0;
|
|
// material length increased
|
|
_materialLen++;
|
|
};
|
|
|
|
for (auto *i : bones) {
|
|
bone = i;
|
|
auto boneCount = _frameData->getBoneCount();
|
|
BoneData *boneData = _frameData->buildBoneData(boneCount);
|
|
auto &boneOriginMat = bone->globalTransformMatrix;
|
|
auto &matm = boneData->globalTransformMatrix.m;
|
|
matm[0] = boneOriginMat.a;
|
|
matm[1] = boneOriginMat.b;
|
|
matm[4] = -boneOriginMat.c;
|
|
matm[5] = -boneOriginMat.d;
|
|
matm[12] = boneOriginMat.tx;
|
|
matm[13] = boneOriginMat.ty;
|
|
}
|
|
|
|
for (auto *i : slots) {
|
|
slot = dynamic_cast<CCSlot *>(i); // TODO(zhakasi): refine the logic
|
|
if (slot == nullptr) {
|
|
return;
|
|
}
|
|
if (!slot->getVisible()) {
|
|
continue;
|
|
}
|
|
slot->updateWorldMatrix();
|
|
|
|
Mat4 *worldMatrix = &slot->worldMatrix;
|
|
|
|
// If slots has child armature,will traverse child first.
|
|
Armature *childArmature = slot->getChildArmature();
|
|
if (childArmature != nullptr) {
|
|
traverseArmature(childArmature, parentOpacity * static_cast<float>(slot->color.a) / 255.0F);
|
|
continue;
|
|
}
|
|
|
|
texture = slot->getTexture();
|
|
if (!texture) continue;
|
|
_curTextureIndex = texture->getRealTextureIndex();
|
|
|
|
auto vbSize = slot->triangles.vertCount * sizeof(middleware::V3F_T2F_C4B);
|
|
vb.checkSpace(vbSize, true);
|
|
|
|
// If texture or blendMode change,will change material.
|
|
if (_preTextureIndex != _curTextureIndex || _preBlendMode != static_cast<int>(slot->_blendMode)) {
|
|
flush();
|
|
}
|
|
|
|
// Calculation vertex color.
|
|
color.a = static_cast<uint8_t>(slot->color.a * parentOpacity);
|
|
color.r = static_cast<uint8_t>(slot->color.r);
|
|
color.g = static_cast<uint8_t>(slot->color.g);
|
|
color.b = static_cast<uint8_t>(slot->color.b);
|
|
|
|
if (preColor != color) {
|
|
preColor = color;
|
|
auto colorCount = _frameData->getColorCount();
|
|
if (colorCount > 0) {
|
|
ColorData *preColorData = _frameData->buildColorData(colorCount - 1);
|
|
preColorData->vertexFloatOffset = vb.getCurPos() / sizeof(float);
|
|
}
|
|
ColorData *colorData = _frameData->buildColorData(colorCount);
|
|
colorData->color = color;
|
|
}
|
|
|
|
// Transform component matrix to global matrix
|
|
middleware::Triangles &triangles = slot->triangles;
|
|
middleware::V3F_T2F_C4B *worldTriangles = slot->worldVerts;
|
|
|
|
for (int v = 0, w = 0, vn = triangles.vertCount; v < vn; ++v, w += 2) {
|
|
middleware::V3F_T2F_C4B *vertex = triangles.verts + v;
|
|
middleware::V3F_T2F_C4B *worldVertex = worldTriangles + v;
|
|
|
|
vertex->vertex.z = 0; //reset for z value
|
|
worldVertex->vertex.transformMat4(vertex->vertex, *worldMatrix);
|
|
|
|
worldVertex->color.r = color.r;
|
|
worldVertex->color.g = color.g;
|
|
worldVertex->color.b = color.b;
|
|
worldVertex->color.a = color.a;
|
|
}
|
|
|
|
vb.writeBytes(reinterpret_cast<char *>(worldTriangles), vbSize);
|
|
|
|
auto ibSize = triangles.indexCount * sizeof(uint16_t);
|
|
ib.checkSpace(ibSize, true);
|
|
|
|
auto vertexOffset = _curVSegLen / VF_XYZUVC;
|
|
for (int ii = 0, nn = triangles.indexCount; ii < nn; ii++) {
|
|
ib.writeUint16(triangles.indices[ii] + vertexOffset);
|
|
}
|
|
|
|
_curISegLen += triangles.indexCount;
|
|
_curVSegLen += static_cast<int32_t>(vbSize / sizeof(float));
|
|
} // End slot traverse
|
|
}
|
|
|
|
void ArmatureCache::resetAllAnimationData() {
|
|
for (auto &animationCache : _animationCaches) {
|
|
animationCache.second->reset();
|
|
}
|
|
}
|
|
|
|
void ArmatureCache::resetAnimationData(const std::string &animationName) {
|
|
for (auto &animationCache : _animationCaches) {
|
|
if (animationCache.second->_animationName == animationName) {
|
|
animationCache.second->reset();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CCArmatureDisplay *ArmatureCache::getArmatureDisplay() {
|
|
return _armatureDisplay;
|
|
}
|
|
|
|
DRAGONBONES_NAMESPACE_END
|
|
|