cocos_lib/cocos/editor-support/spine-creator-support/SkeletonRenderer.cpp

1125 lines
44 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 "spine-creator-support/SkeletonRenderer.h"
#include <algorithm>
#include "2d/renderer/RenderDrawInfo.h"
#include "2d/renderer/RenderEntity.h"
#include "MiddlewareMacro.h"
#include "SharedBufferManager.h"
#include "SkeletonDataMgr.h"
#include "base/DeferredReleasePool.h"
#include "base/TypeDef.h"
#include "base/memory/Memory.h"
#include "gfx-base/GFXDef.h"
#include "math/Math.h"
#include "math/Vec3.h"
#include "renderer/core/MaterialInstance.h"
#include "spine-creator-support/AttachmentVertices.h"
#include "spine-creator-support/spine-cocos2dx.h"
USING_NS_MW; // NOLINT(google-build-using-namespace)
using namespace spine; // NOLINT(google-build-using-namespace)
using namespace cc; // NOLINT(google-build-using-namespace)
using namespace cc::gfx; // NOLINT(google-build-using-namespace)
using std::max;
using std::min;
static const std::string TECH_STAGE = "opaque";
static const std::string TEXTURE_KEY = "texture";
static spine::Cocos2dTextureLoader textureLoader;
static std::vector<middleware::Texture2D *> _slotTextureSet{};
enum DebugType {
NONE = 0,
SLOTS,
MESH,
BONES
};
SkeletonRenderer *SkeletonRenderer::create() {
return new SkeletonRenderer();
}
SkeletonRenderer *SkeletonRenderer::createWithSkeleton(Skeleton *skeleton, bool ownsSkeleton, bool ownsSkeletonData) {
return new SkeletonRenderer(skeleton, ownsSkeleton, ownsSkeletonData);
}
SkeletonRenderer *SkeletonRenderer::createWithData(SkeletonData *skeletonData, bool ownsSkeletonData) {
return new SkeletonRenderer(skeletonData, ownsSkeletonData);
}
SkeletonRenderer *SkeletonRenderer::createWithFile(const std::string &skeletonDataFile, const std::string &atlasFile, float scale) {
return new SkeletonRenderer(skeletonDataFile, atlasFile, scale);
}
void SkeletonRenderer::initialize() {
if (_clipper == nullptr) {
_clipper = new (__FILE__, __LINE__) SkeletonClipping();
}
if (_sharedBufferOffset == nullptr) {
// store global TypedArray begin and end offset
_sharedBufferOffset = new cc::middleware::IOTypedArray(se::Object::TypedArrayType::UINT32, sizeof(uint32_t) * 2);
}
_skeleton->setToSetupPose();
_skeleton->updateWorldTransform();
}
void SkeletonRenderer::beginSchedule() {
MiddlewareManager::getInstance()->addTimer(this);
}
void SkeletonRenderer::onEnable() {
beginSchedule();
}
void SkeletonRenderer::onDisable() {
stopSchedule();
}
void SkeletonRenderer::stopSchedule() {
MiddlewareManager::getInstance()->removeTimer(this);
if (_sharedBufferOffset) {
_sharedBufferOffset->reset();
_sharedBufferOffset->clear();
}
if (_debugBuffer) {
_debugBuffer->reset();
_debugBuffer->clear();
}
}
void SkeletonRenderer::setSkeletonData(SkeletonData *skeletonData, bool ownsSkeletonData) {
_skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData);
_ownsSkeletonData = ownsSkeletonData;
}
SkeletonRenderer::SkeletonRenderer() = default;
SkeletonRenderer::SkeletonRenderer(Skeleton *skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) {
initWithSkeleton(skeleton, ownsSkeleton, ownsSkeletonData, ownsAtlas);
}
SkeletonRenderer::SkeletonRenderer(SkeletonData *skeletonData, bool ownsSkeletonData) {
initWithData(skeletonData, ownsSkeletonData);
}
SkeletonRenderer::SkeletonRenderer(const std::string &skeletonDataFile, const std::string &atlasFile, float scale) {
initWithJsonFile(skeletonDataFile, atlasFile, scale);
}
SkeletonRenderer::~SkeletonRenderer() {
CC_SAFE_RELEASE(_effectDelegate);
if (_ownsSkeletonData) delete _skeleton->getData();
if (_ownsSkeleton) delete _skeleton;
if (_ownsAtlas && _atlas) delete _atlas;
delete _attachmentLoader;
delete _clipper;
if (_debugBuffer) {
delete _debugBuffer;
_debugBuffer = nullptr;
}
if (_sharedBufferOffset) {
delete _sharedBufferOffset;
_sharedBufferOffset = nullptr;
}
for (auto *draw : _drawInfoArray) {
CC_SAFE_DELETE(draw);
}
for (auto &item : _materialCaches) {
CC_SAFE_DELETE(item.second);
}
stopSchedule();
}
void SkeletonRenderer::initWithUUID(const std::string &uuid) {
_ownsSkeleton = true;
_uuid = uuid;
SkeletonData *skeletonData = SkeletonDataMgr::getInstance()->retainByUUID(uuid);
CC_ASSERT(skeletonData);
setSkeletonData(skeletonData, false);
initialize();
}
void SkeletonRenderer::initWithSkeleton(Skeleton *skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) {
_skeleton = skeleton;
_ownsSkeleton = ownsSkeleton;
_ownsSkeletonData = ownsSkeletonData;
_ownsAtlas = ownsAtlas;
initialize();
}
void SkeletonRenderer::initWithData(SkeletonData *skeletonData, bool ownsSkeletonData) {
_ownsSkeleton = true;
setSkeletonData(skeletonData, ownsSkeletonData);
initialize();
}
void SkeletonRenderer::initWithJsonFile(const std::string &skeletonDataFile, Atlas *atlas, float scale) {
_atlas = atlas;
_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
SkeletonJson json(_attachmentLoader);
json.setScale(scale);
SkeletonData *skeletonData = json.readSkeletonDataFile(skeletonDataFile.c_str());
CC_ASSERT(skeletonData); // Can use json.getError() to get error message.
_ownsSkeleton = true;
setSkeletonData(skeletonData, true);
initialize();
}
void SkeletonRenderer::initWithJsonFile(const std::string &skeletonDataFile, const std::string &atlasFile, float scale) {
_atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader);
CC_ASSERT(_atlas);
_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
SkeletonJson json(_attachmentLoader);
json.setScale(scale);
SkeletonData *skeletonData = json.readSkeletonDataFile(skeletonDataFile.c_str());
CC_ASSERT(skeletonData); // Can use json.getError() to get error message.
_ownsSkeleton = true;
_ownsAtlas = true;
setSkeletonData(skeletonData, true);
initialize();
}
void SkeletonRenderer::initWithBinaryFile(const std::string &skeletonDataFile, Atlas *atlas, float scale) {
_atlas = atlas;
_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
SkeletonBinary binary(_attachmentLoader);
binary.setScale(scale);
SkeletonData *skeletonData = binary.readSkeletonDataFile(skeletonDataFile.c_str());
CC_ASSERT(skeletonData); // Can use binary.getError() to get error message.
_ownsSkeleton = true;
setSkeletonData(skeletonData, true);
initialize();
}
void SkeletonRenderer::initWithBinaryFile(const std::string &skeletonDataFile, const std::string &atlasFile, float scale) {
_atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader);
CC_ASSERT(_atlas);
_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
SkeletonBinary binary(_attachmentLoader);
binary.setScale(scale);
SkeletonData *skeletonData = binary.readSkeletonDataFile(skeletonDataFile.c_str());
CC_ASSERT(skeletonData); // Can use binary.getError() to get error message.
_ownsSkeleton = true;
_ownsAtlas = true;
setSkeletonData(skeletonData, true);
initialize();
}
void SkeletonRenderer::render(float /*deltaTime*/) {
if (!_skeleton) return;
auto *entity = _entity;
entity->clearDynamicRenderDrawInfos();
_sharedBufferOffset->reset();
_sharedBufferOffset->clear();
// avoid other place call update.
auto *mgr = MiddlewareManager::getInstance();
if (!mgr->isRendering) return;
auto *attachMgr = mgr->getAttachInfoMgr();
auto *attachInfo = attachMgr->getBuffer();
if (!attachInfo) return;
// store attach info offset
_sharedBufferOffset->writeUint32(static_cast<uint32_t>(attachInfo->getCurPos()) / sizeof(uint32_t));
// If opacity is 0,then return.
if (_skeleton->getColor().a == 0) {
return;
}
auto &nodeWorldMat = entity->getNode()->getWorldMatrix();
// color range is [0.0, 1.0]
cc::middleware::Color4F color;
cc::middleware::Color4F darkColor;
AttachmentVertices *attachmentVertices = nullptr;
bool inRange = !(_startSlotIndex != -1 || _endSlotIndex != -1);
auto vertexFormat = _useTint ? VF_XYZUVCC : VF_XYZUVC;
cc::middleware::MeshBuffer *mb = mgr->getMeshBuffer(vertexFormat);
cc::middleware::IOBuffer &vb = mb->getVB();
cc::middleware::IOBuffer &ib = mb->getIB();
// vertex size int bytes with one color
unsigned int vbs1 = sizeof(V3F_T2F_C4B);
// vertex size in floats with one color
unsigned int vs1 = vbs1 / sizeof(float);
// vertex size int bytes with two color
unsigned int vbs2 = sizeof(V3F_T2F_C4B_C4B);
// verex size in floats with two color
unsigned int vs2 = vbs2 / sizeof(float);
auto vbs = vbs1;
if (_useTint) {
vbs = vbs2;
}
unsigned int vbSize = 0;
unsigned int ibSize = 0;
int curBlendSrc = -1;
int curBlendDst = -1;
int curBlendMode = -1;
int preBlendMode = -1;
uint32_t curISegLen = 0;
cc::Texture2D *preTexture = nullptr;
cc::Texture2D *curTexture = nullptr;
RenderDrawInfo *curDrawInfo = nullptr;
int materialLen = 0;
Slot *slot = nullptr;
int isFull = 0;
if (_debugSlots || _debugBones || _debugMesh) {
// If enable debug draw,then init debug buffer.
if (_debugBuffer == nullptr) {
_debugBuffer = new cc::middleware::IOTypedArray(se::Object::TypedArrayType::FLOAT32, MAX_DEBUG_BUFFER_SIZE);
}
_debugBuffer->reset();
}
auto flush = [&]() {
// fill pre segment indices count field
if (curDrawInfo) {
curDrawInfo->setIbCount(curISegLen);
}
curDrawInfo = requestDrawInfo(materialLen);
entity->addDynamicRenderDrawInfo(curDrawInfo);
// prepare to fill new segment field
curBlendMode = slot->getData().getBlendMode();
switch (curBlendMode) {
case BlendMode_Additive:
curBlendSrc = static_cast<int>(_premultipliedAlpha ? BlendFactor::ONE : BlendFactor::SRC_ALPHA);
curBlendDst = static_cast<int>(BlendFactor::ONE);
break;
case BlendMode_Multiply:
curBlendSrc = static_cast<int>(BlendFactor::DST_COLOR);
curBlendDst = static_cast<int>(BlendFactor::ONE_MINUS_SRC_ALPHA);
break;
case BlendMode_Screen:
curBlendSrc = static_cast<int>(BlendFactor::ONE);
curBlendDst = static_cast<int>(BlendFactor::ONE_MINUS_SRC_COLOR);
break;
default:
curBlendSrc = static_cast<int>(_premultipliedAlpha ? BlendFactor::ONE : BlendFactor::SRC_ALPHA);
curBlendDst = static_cast<int>(BlendFactor::ONE_MINUS_SRC_ALPHA);
}
auto *material = requestMaterial(curBlendSrc, curBlendDst);
curDrawInfo->setMaterial(material);
gfx::Texture *texture = curTexture->getGFXTexture();
gfx::Sampler *sampler = curTexture->getGFXSampler();
curDrawInfo->setTexture(texture);
curDrawInfo->setSampler(sampler);
auto *uiMeshBuffer = mb->getUIMeshBuffer();
curDrawInfo->setMeshBuffer(uiMeshBuffer);
curDrawInfo->setIndexOffset(static_cast<uint32_t>(ib.getCurPos()) / sizeof(uint16_t));
// reset pre blend mode to current
preBlendMode = static_cast<int>(slot->getData().getBlendMode());
// reset pre texture index to current
preTexture = curTexture;
// reset index segmentation count
curISegLen = 0;
// material length increased
materialLen++;
};
VertexEffect *effect = nullptr;
if (_effectDelegate) {
effect = _effectDelegate->getVertexEffect();
}
if (effect) {
effect->begin(*_skeleton);
}
auto &drawOrder = _skeleton->getDrawOrder();
for (size_t i = 0, n = drawOrder.size(); i < n; ++i) {
isFull = 0;
slot = drawOrder[i];
if (slot->getBone().isActive() == false) {
continue;
}
if (_startSlotIndex >= 0 && _startSlotIndex == slot->getData().getIndex()) {
inRange = true;
}
if (!inRange) {
_clipper->clipEnd(*slot);
continue;
}
if (_endSlotIndex >= 0 && _endSlotIndex == slot->getData().getIndex()) {
inRange = false;
}
if (!slot->getAttachment()) {
_clipper->clipEnd(*slot);
continue;
}
cc::middleware::Triangles triangles;
cc::middleware::TwoColorTriangles trianglesTwoColor;
if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) {
auto *attachment = dynamic_cast<RegionAttachment *>(slot->getAttachment());
attachmentVertices = reinterpret_cast<AttachmentVertices *>(attachment->getRendererObject());
// Early exit if attachment is invisible
if (attachment->getColor().a == 0) {
_clipper->clipEnd(*slot);
continue;
}
if (!_useTint) {
triangles.vertCount = attachmentVertices->_triangles->vertCount;
vbSize = triangles.vertCount * sizeof(V3F_T2F_C4B);
isFull |= vb.checkSpace(vbSize, true);
triangles.verts = reinterpret_cast<V3F_T2F_C4B *>(vb.getCurBuffer());
memcpy(static_cast<void *>(triangles.verts), static_cast<void *>(attachmentVertices->_triangles->verts), vbSize);
attachment->computeWorldVertices(slot->getBone(), reinterpret_cast<float *>(triangles.verts), 0, vs1);
triangles.indexCount = attachmentVertices->_triangles->indexCount;
ibSize = triangles.indexCount * sizeof(uint16_t);
ib.checkSpace(ibSize, true);
triangles.indices = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
memcpy(triangles.indices, attachmentVertices->_triangles->indices, ibSize);
} else {
trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount;
vbSize = trianglesTwoColor.vertCount * sizeof(V3F_T2F_C4B_C4B);
isFull |= 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 = trianglesTwoColor.indexCount * sizeof(uint16_t);
ib.checkSpace(ibSize, true);
trianglesTwoColor.indices = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
memcpy(trianglesTwoColor.indices, attachmentVertices->_triangles->indices, ibSize);
}
color.r = attachment->getColor().r;
color.g = attachment->getColor().g;
color.b = attachment->getColor().b;
color.a = attachment->getColor().a;
if (_debugSlots) {
_debugBuffer->writeFloat32(DebugType::SLOTS);
_debugBuffer->writeFloat32(8);
float *vertices = _useTint ? reinterpret_cast<float *>(trianglesTwoColor.verts) : reinterpret_cast<float *>(triangles.verts);
unsigned int stride = _useTint ? vs2 : vs1;
// Quadrangle has 4 vertex.
for (int ii = 0; ii < 4; ii++) {
_debugBuffer->writeFloat32(vertices[0]);
_debugBuffer->writeFloat32(vertices[1]);
vertices += stride;
}
}
} 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;
}
if (!_useTint) {
triangles.vertCount = attachmentVertices->_triangles->vertCount;
vbSize = triangles.vertCount * sizeof(V3F_T2F_C4B);
isFull |= vb.checkSpace(vbSize, true);
triangles.verts = reinterpret_cast<V3F_T2F_C4B *>(vb.getCurBuffer());
memcpy(static_cast<void *>(triangles.verts), static_cast<void *>(attachmentVertices->_triangles->verts), vbSize);
attachment->computeWorldVertices(*slot, 0, attachment->getWorldVerticesLength(), reinterpret_cast<float *>(triangles.verts), 0, vs1);
triangles.indexCount = attachmentVertices->_triangles->indexCount;
ibSize = triangles.indexCount * sizeof(uint16_t);
ib.checkSpace(ibSize, true);
triangles.indices = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
memcpy(triangles.indices, attachmentVertices->_triangles->indices, ibSize);
} else {
trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount;
vbSize = trianglesTwoColor.vertCount * sizeof(V3F_T2F_C4B_C4B);
isFull |= 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 = trianglesTwoColor.indexCount * sizeof(uint16_t);
ib.checkSpace(ibSize, true);
trianglesTwoColor.indices = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
memcpy(trianglesTwoColor.indices, attachmentVertices->_triangles->indices, ibSize);
}
color.r = attachment->getColor().r;
color.g = attachment->getColor().g;
color.b = attachment->getColor().b;
color.a = attachment->getColor().a;
if (_debugMesh) {
int indexCount = _useTint ? trianglesTwoColor.indexCount : triangles.indexCount;
uint16_t *indices = _useTint ? trianglesTwoColor.indices : triangles.indices;
float *vertices = _useTint ? reinterpret_cast<float *>(trianglesTwoColor.verts) : reinterpret_cast<float *>(triangles.verts);
unsigned int stride = _useTint ? vs2 : vs1;
_debugBuffer->writeFloat32(DebugType::MESH);
_debugBuffer->writeFloat32(static_cast<float>(indexCount * 2));
for (int ii = 0; ii < indexCount; ii += 3) {
unsigned int v1 = indices[ii] * stride;
unsigned int v2 = indices[ii + 1] * stride;
unsigned int v3 = indices[ii + 2] * stride;
_debugBuffer->writeFloat32(vertices[v1]);
_debugBuffer->writeFloat32(vertices[v1 + 1]);
_debugBuffer->writeFloat32(vertices[v2]);
_debugBuffer->writeFloat32(vertices[v2 + 1]);
_debugBuffer->writeFloat32(vertices[v3]);
_debugBuffer->writeFloat32(vertices[v3 + 1]);
}
}
} 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 * slot->getColor().a * color.a * _nodeColor.a * 255;
// skip rendering if the color of this attachment is 0
if (color.a == 0) {
_clipper->clipEnd(*slot);
continue;
}
float multiplier = _premultipliedAlpha ? color.a : 255;
float red = _nodeColor.r * _skeleton->getColor().r * color.r * multiplier;
float green = _nodeColor.g * _skeleton->getColor().g * color.g * multiplier;
float blue = _nodeColor.b * _skeleton->getColor().b * color.b * multiplier;
color.r = red * slot->getColor().r;
color.g = green * slot->getColor().g;
color.b = blue * slot->getColor().b;
if (slot->hasDarkColor()) {
darkColor.r = red * slot->getDarkColor().r;
darkColor.g = green * slot->getDarkColor().g;
darkColor.b = blue * slot->getDarkColor().b;
} else {
darkColor.r = 0;
darkColor.g = 0;
darkColor.b = 0;
}
darkColor.a = _premultipliedAlpha ? 255 : 0;
// One color tint logic
if (!_useTint) {
// Cliping logic
Color4B light;
light.r = (uint8_t)color.r;
light.g = (uint8_t)color.g;
light.b = (uint8_t)color.b;
light.a = (uint8_t)color.a;
if (_clipper->isClipping()) {
_clipper->clipTriangles(reinterpret_cast<float *>(&triangles.verts[0].vertex), triangles.indices, triangles.indexCount, reinterpret_cast<float *>(&triangles.verts[0].texCoord), vs1);
if (_clipper->getClippedTriangles().size() == 0) {
_clipper->clipEnd(*slot);
continue;
}
triangles.vertCount = static_cast<int>(_clipper->getClippedVertices().size()) >> 1;
vbSize = triangles.vertCount * sizeof(V3F_T2F_C4B);
isFull |= vb.checkSpace(vbSize, true);
triangles.verts = reinterpret_cast<V3F_T2F_C4B *>(vb.getCurBuffer());
triangles.indexCount = static_cast<int>(_clipper->getClippedTriangles().size());
ibSize = triangles.indexCount * sizeof(uint16_t);
ib.checkSpace(ibSize, true);
triangles.indices = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
memcpy(triangles.indices, _clipper->getClippedTriangles().buffer(), sizeof(uint16_t) * _clipper->getClippedTriangles().size());
float *verts = _clipper->getClippedVertices().buffer();
float *uvs = _clipper->getClippedUVs().buffer();
if (effect) {
for (int v = 0, vn = triangles.vertCount, vv = 0; v < vn; ++v, vv += 2) {
V3F_T2F_C4B *vertex = triangles.verts + v;
vertex->vertex.x = verts[vv];
vertex->vertex.y = verts[vv + 1];
vertex->texCoord.u = uvs[vv];
vertex->texCoord.v = uvs[vv + 1];
effect->transform(vertex->vertex.x, vertex->vertex.y);
vertex->color = light;
}
} else {
for (int v = 0, vn = triangles.vertCount, vv = 0; v < vn; ++v, vv += 2) {
V3F_T2F_C4B *vertex = triangles.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 = light;
}
}
// No cliping logic
} else {
if (effect) {
for (int v = 0, vn = triangles.vertCount; v < vn; ++v) {
V3F_T2F_C4B *vertex = triangles.verts + v;
effect->transform(vertex->vertex.x, vertex->vertex.y);
vertex->color = light;
}
} else {
for (int v = 0, vn = triangles.vertCount; v < vn; ++v) {
V3F_T2F_C4B *vertex = triangles.verts + v;
vertex->color = light;
}
}
}
}
// Two color tint logic
else {
Color4B light;
Color4B dark;
light.r = (uint8_t)color.r;
light.g = (uint8_t)color.g;
light.b = (uint8_t)color.b;
light.a = (uint8_t)color.a;
dark.r = (uint8_t)darkColor.r;
dark.g = (uint8_t)darkColor.g;
dark.b = (uint8_t)darkColor.b;
dark.a = (uint8_t)darkColor.a;
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 = trianglesTwoColor.vertCount * sizeof(V3F_T2F_C4B_C4B);
isFull |= vb.checkSpace(vbSize, true);
trianglesTwoColor.verts = reinterpret_cast<V3F_T2F_C4B_C4B *>(vb.getCurBuffer());
trianglesTwoColor.indexCount = static_cast<int>(_clipper->getClippedTriangles().size());
ibSize = trianglesTwoColor.indexCount * sizeof(uint16_t);
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();
if (effect) {
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];
effect->transform(vertex->vertex.x, vertex->vertex.y);
vertex->color = light;
vertex->color2 = dark;
}
} else {
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 = light;
vertex->color2 = dark;
}
}
} else {
if (effect) {
for (int v = 0, vn = trianglesTwoColor.vertCount; v < vn; ++v) {
V3F_T2F_C4B_C4B *vertex = trianglesTwoColor.verts + v;
effect->transform(vertex->vertex.x, vertex->vertex.y);
vertex->color = light;
vertex->color2 = dark;
}
} else {
for (int v = 0, vn = trianglesTwoColor.vertCount; v < vn; ++v) {
V3F_T2F_C4B_C4B *vertex = trianglesTwoColor.verts + v;
vertex->color = light;
vertex->color2 = dark;
}
}
}
}
curTexture = (cc::Texture2D *)attachmentVertices->_texture->getRealTexture();
// If texture or blendMode change,will change material.
if (preTexture != curTexture || preBlendMode != slot->getData().getBlendMode() || isFull) {
flush();
}
if (_enableBatch) {
uint8_t *vbBuffer = vb.getCurBuffer();
cc::Vec3 *point = nullptr;
for (unsigned int ii = 0, nn = vbSize; ii < nn; ii += vbs) {
point = reinterpret_cast<cc::Vec3 *>(vbBuffer + ii);
point->z = 0;
point->transformMat4(*point, nodeWorldMat);
}
}
auto vertexOffset = vb.getCurPos() / vbs;
if (vbSize > 0 && ibSize > 0) {
if (vertexOffset > 0) {
auto *ibBuffer = reinterpret_cast<uint16_t *>(ib.getCurBuffer());
for (unsigned int ii = 0, nn = ibSize / sizeof(uint16_t); ii < nn; ii++) {
ibBuffer[ii] += vertexOffset;
}
}
vb.move(static_cast<int>(vbSize));
ib.move(static_cast<int>(ibSize));
// Record this turn index segmentation count,it will store in material buffer in the end.
curISegLen += ibSize / sizeof(uint16_t);
}
_clipper->clipEnd(*slot);
} // End slot traverse
_clipper->clipEnd();
if (effect) effect->end();
if (curDrawInfo) curDrawInfo->setIbCount(curISegLen);
if (_useAttach || _debugBones) {
auto &bones = _skeleton->getBones();
size_t bonesCount = bones.size();
cc::Mat4 boneMat = cc::Mat4::IDENTITY;
if (_debugBones) {
_debugBuffer->writeFloat32(DebugType::BONES);
_debugBuffer->writeFloat32(static_cast<float>(bonesCount * 4));
}
for (size_t i = 0, n = bonesCount; i < n; i++) {
Bone *bone = bones[i];
boneMat.m[0] = bone->getA();
boneMat.m[1] = bone->getC();
boneMat.m[4] = bone->getB();
boneMat.m[5] = bone->getD();
boneMat.m[12] = bone->getWorldX();
boneMat.m[13] = bone->getWorldY();
attachInfo->checkSpace(sizeof(boneMat), true);
attachInfo->writeBytes(reinterpret_cast<const char *>(&boneMat), sizeof(boneMat));
if (_debugBones) {
float boneLength = bone->getData().getLength();
float x = boneLength * bone->getA() + bone->getWorldX();
float y = boneLength * bone->getC() + bone->getWorldY();
_debugBuffer->writeFloat32(bone->getWorldX());
_debugBuffer->writeFloat32(bone->getWorldY());
_debugBuffer->writeFloat32(x);
_debugBuffer->writeFloat32(y);
}
}
}
// debug end
if (_debugBuffer) {
if (_debugBuffer->isOutRange()) {
_debugBuffer->reset();
CC_LOG_INFO("Spine debug data is too large, debug buffer has no space to put in it!!!!!!!!!!");
CC_LOG_INFO("You can adjust MAX_DEBUG_BUFFER_SIZE macro");
}
_debugBuffer->writeFloat32(DebugType::NONE);
}
}
cc::Rect SkeletonRenderer::getBoundingBox() const {
static cc::middleware::IOBuffer buffer(1024);
float *worldVertices = nullptr;
float minX = 999999.0F;
float minY = 999999.0F;
float maxX = -999999.0F;
float maxY = -999999.0F;
for (int i = 0; i < _skeleton->getSlots().size(); ++i) {
Slot *slot = _skeleton->getSlots()[i];
if (!slot->getAttachment()) continue;
int verticesCount;
if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) {
auto *attachment = dynamic_cast<RegionAttachment *>(slot->getAttachment());
buffer.checkSpace(8 * sizeof(float));
worldVertices = reinterpret_cast<float *>(buffer.getCurBuffer());
attachment->computeWorldVertices(slot->getBone(), worldVertices, 0, 2);
verticesCount = 8;
} else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) {
auto *mesh = dynamic_cast<MeshAttachment *>(slot->getAttachment());
buffer.checkSpace(mesh->getWorldVerticesLength() * sizeof(float));
worldVertices = reinterpret_cast<float *>(buffer.getCurBuffer());
mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), worldVertices, 0, 2);
verticesCount = static_cast<int>(mesh->getWorldVerticesLength());
} else {
continue;
}
for (int ii = 0; ii < verticesCount; ii += 2) {
float x = worldVertices[ii];
float y = worldVertices[ii + 1];
minX = min(minX, x);
minY = min(minY, y);
maxX = max(maxX, x);
maxY = max(maxY, y);
}
}
if (minX == 999999.0F) minX = minY = maxX = maxY = 0;
return cc::Rect(minX, minY, maxX - minX, maxY - minY);
}
void SkeletonRenderer::updateWorldTransform() {
if (_skeleton) {
_skeleton->updateWorldTransform();
}
}
void SkeletonRenderer::setAttachEnabled(bool enabled) {
_useAttach = enabled;
}
void SkeletonRenderer::setToSetupPose() {
if (_skeleton) {
_skeleton->setToSetupPose();
}
}
void SkeletonRenderer::setBonesToSetupPose() {
if (_skeleton) {
_skeleton->setBonesToSetupPose();
}
}
void SkeletonRenderer::setSlotsToSetupPose() {
if (_skeleton) {
_skeleton->setSlotsToSetupPose();
}
}
spine::Bone *SkeletonRenderer::findBone(const std::string &boneName) const {
if (_skeleton) {
return _skeleton->findBone(boneName.c_str());
}
return nullptr;
}
spine::Slot *SkeletonRenderer::findSlot(const std::string &slotName) const {
if (_skeleton) {
return _skeleton->findSlot(slotName.c_str());
}
return nullptr;
}
void SkeletonRenderer::setSkin(const std::string &skinName) {
if (_skeleton) {
_skeleton->setSkin(skinName.empty() ? nullptr : skinName.c_str());
_skeleton->setSlotsToSetupPose();
}
}
void SkeletonRenderer::setSkin(const char *skinName) {
if (_skeleton) {
_skeleton->setSkin(skinName);
_skeleton->setSlotsToSetupPose();
}
}
spine::Attachment *SkeletonRenderer::getAttachment(const std::string &slotName, const std::string &attachmentName) const {
if (_skeleton) {
return _skeleton->getAttachment(slotName.c_str(), attachmentName.c_str());
}
return nullptr;
}
bool SkeletonRenderer::setAttachment(const std::string &slotName, const std::string &attachmentName) {
if (_skeleton) {
_skeleton->setAttachment(slotName.c_str(), attachmentName.empty() ? nullptr : attachmentName.c_str());
}
return true;
}
bool SkeletonRenderer::setAttachment(const std::string &slotName, const char *attachmentName) {
if (_skeleton) {
_skeleton->setAttachment(slotName.c_str(), attachmentName);
}
return true;
}
void SkeletonRenderer::setUseTint(bool enabled) {
_useTint = enabled;
}
void SkeletonRenderer::setVertexEffectDelegate(VertexEffectDelegate *effectDelegate) {
if (_effectDelegate == effectDelegate) {
return;
}
CC_SAFE_RELEASE(_effectDelegate);
_effectDelegate = effectDelegate;
CC_SAFE_ADD_REF(_effectDelegate);
}
void SkeletonRenderer::setSlotsRange(int startSlotIndex, int endSlotIndex) {
this->_startSlotIndex = startSlotIndex;
this->_endSlotIndex = endSlotIndex;
}
spine::Skeleton *SkeletonRenderer::getSkeleton() const {
return _skeleton;
}
void SkeletonRenderer::setTimeScale(float scale) {
_timeScale = scale;
}
float SkeletonRenderer::getTimeScale() const {
return _timeScale;
}
void SkeletonRenderer::paused(bool value) {
_paused = value;
}
void SkeletonRenderer::setColor(float r, float g, float b, float a) {
_nodeColor.r = r / 255.0F;
_nodeColor.g = g / 255.0F;
_nodeColor.b = b / 255.0F;
_nodeColor.a = a / 255.0F;
}
void SkeletonRenderer::setBatchEnabled(bool enabled) {
if (enabled != _enableBatch) {
for (auto &item : _materialCaches) {
CC_SAFE_DELETE(item.second);
}
_materialCaches.clear();
_enableBatch = enabled;
}
}
void SkeletonRenderer::setDebugBonesEnabled(bool enabled) {
_debugBones = enabled;
}
void SkeletonRenderer::setDebugSlotsEnabled(bool enabled) {
_debugSlots = enabled;
}
void SkeletonRenderer::setDebugMeshEnabled(bool enabled) {
_debugMesh = enabled;
}
void SkeletonRenderer::setOpacityModifyRGB(bool value) {
_premultipliedAlpha = value;
}
bool SkeletonRenderer::isOpacityModifyRGB() const {
return _premultipliedAlpha;
}
se_object_ptr SkeletonRenderer::getDebugData() const {
if (_debugBuffer) {
return _debugBuffer->getTypeArray();
}
return nullptr;
}
se_object_ptr SkeletonRenderer::getSharedBufferOffset() const {
if (_sharedBufferOffset) {
return _sharedBufferOffset->getTypeArray();
}
return nullptr;
}
void SkeletonRenderer::setRenderEntity(cc::RenderEntity *entity) {
_entity = entity;
}
void SkeletonRenderer::setMaterial(cc::Material *material) {
_material = material;
for (auto &item : _materialCaches) {
CC_SAFE_DELETE(item.second);
}
_materialCaches.clear();
}
cc::RenderDrawInfo *SkeletonRenderer::requestDrawInfo(int idx) {
if (_drawInfoArray.size() < idx + 1) {
cc::RenderDrawInfo *draw = new cc::RenderDrawInfo();
draw->setDrawInfoType(static_cast<uint32_t>(RenderDrawInfoType::MIDDLEWARE));
_drawInfoArray.push_back(draw);
}
return _drawInfoArray[idx];
}
cc::Material *SkeletonRenderer::requestMaterial(uint16_t blendSrc, uint16_t blendDst) {
uint32_t key = static_cast<uint32_t>(blendSrc) << 16 | static_cast<uint32_t>(blendDst);
if (_materialCaches.find(key) == _materialCaches.end()) {
const IMaterialInstanceInfo info{
(Material *)_material,
0};
MaterialInstance *materialInstance = new MaterialInstance(info);
PassOverrides overrides;
BlendStateInfo stateInfo;
stateInfo.blendColor = gfx::Color{1.0F, 1.0F, 1.0F, 1.0F};
BlendTargetInfo targetInfo;
targetInfo.blendEq = gfx::BlendOp::ADD;
targetInfo.blendAlphaEq = gfx::BlendOp::ADD;
targetInfo.blendSrc = (gfx::BlendFactor)blendSrc;
targetInfo.blendDst = (gfx::BlendFactor)blendDst;
targetInfo.blendSrcAlpha = (gfx::BlendFactor)blendSrc;
targetInfo.blendDstAlpha = (gfx::BlendFactor)blendDst;
BlendTargetInfoList targetList{targetInfo};
stateInfo.targets = targetList;
overrides.blendState = stateInfo;
materialInstance->overridePipelineStates(overrides);
const MacroRecord macros{{"TWO_COLORED", _useTint}, {"USE_LOCAL", !_enableBatch}};
materialInstance->recompileShaders(macros);
_materialCaches[key] = materialInstance;
}
return _materialCaches[key];
}
void SkeletonRenderer::setSlotTexture(const std::string &slotName, cc::Texture2D *tex2d, bool createAttachment) {
if (!_skeleton) return;
auto slot = _skeleton->findSlot(slotName.c_str());
if (!slot) return;
auto attachment = slot->getAttachment();
if (!attachment) return;
auto width = tex2d->getWidth();
auto height = tex2d->getHeight();
if (createAttachment) {
attachment = attachment->copy();
slot->setAttachment(attachment);
}
AttachmentVertices *attachmentVertices = nullptr;
if (attachment->getRTTI().isExactly(spine::RegionAttachment::rtti)) {
auto region = static_cast<RegionAttachment *>(attachment);
region->setRegionWidth(width);
region->setRegionHeight(height);
region->setRegionOriginalWidth(width);
region->setRegionOriginalHeight(height);
region->setWidth(width);
region->setHeight(height);
region->setUVs(0, 0, 1.0f, 1.0f, false);
region->updateOffset();
attachmentVertices = static_cast<AttachmentVertices *>(region->getRendererObject());
if (createAttachment) {
attachmentVertices = attachmentVertices->copy();
region->setRendererObject(attachmentVertices);
}
V3F_T2F_C4B *vertices = attachmentVertices->_triangles->verts;
auto UVs = region->getUVs();
for (int i = 0, ii = 0; i < 4; ++i, ii += 2) {
vertices[i].texCoord.u = UVs[ii];
vertices[i].texCoord.v = UVs[ii + 1];
}
} else if (attachment->getRTTI().isExactly(spine::MeshAttachment::rtti)) {
auto mesh = static_cast<MeshAttachment *>(attachment);
mesh->setRegionWidth(width);
mesh->setRegionHeight(height);
mesh->setRegionOriginalWidth(width);
mesh->setRegionOriginalHeight(height);
mesh->setWidth(width);
mesh->setHeight(height);
mesh->setRegionU(0);
mesh->setRegionV(0);
mesh->setRegionU2(1.0f);
mesh->setRegionV2(1.0f);
mesh->setRegionRotate(true);
mesh->setRegionDegrees(0);
mesh->updateUVs();
attachmentVertices = static_cast<AttachmentVertices *>(mesh->getRendererObject());
if (createAttachment) {
attachmentVertices = attachmentVertices->copy();
mesh->setRendererObject(attachmentVertices);
}
V3F_T2F_C4B *vertices = attachmentVertices->_triangles->verts;
auto UVs = mesh->getUVs();
for (size_t i = 0, ii = 0, nn = mesh->getWorldVerticesLength(); ii < nn; ++i, ii += 2) {
vertices[i].texCoord.u = UVs[ii];
vertices[i].texCoord.v = UVs[ii + 1];
}
}
if (!attachmentVertices) return;
middleware::Texture2D *middlewareTexture = nullptr;
for (auto &it : _slotTextureSet) {
if (it->getRealTexture() == tex2d) {
middlewareTexture = it;
break;
}
}
if (!middlewareTexture) {
middlewareTexture = new middleware::Texture2D();
middlewareTexture->addRef();
middlewareTexture->setRealTexture(tex2d);
}
if (attachmentVertices->_texture) {
attachmentVertices->_texture->release();
}
attachmentVertices->_texture = middlewareTexture;
}