前言:
在写本文之前曾在网上找过 cocos2dx spine 动画给 动画slot 增加 node 的方法:http://blog.csdn.net/xzben/article/details/50614335 通过这个方法能够基本能实现我们简单的需求,但是有个缺点就是 增加的slot node 都必然在整个动画上面,不能保持原先动画的 slot zorder,于是有了这篇文章的产生。
注意:
本文内容本人还只做了简单的测试,如果还有其它问题欢迎留言。谢谢。
1、spine 的渲染的解析
void SkeletonRenderer::drawSkeleton (const Mat4 &transform, uint32_t transformFlags) {
getGLProgramState()->apply(transform); // 使用 shader,并指定渲染的 转换矩阵
Color3B nodeColor = getColor();
_skeleton->r = nodeColor.r / (float)255;
_skeleton->g = nodeColor.g / (float)255;
_skeleton->b = nodeColor.b / (float)255;
_skeleton->a = getDisplayedOpacity() / (float)255;
int blendMode = -1;
Color4B color;
const float* uvs = nullptr;
int verticesCount = 0;
const int* triangles = nullptr;
int trianglesCount = 0;
float r = 0, g = 0, b = 0, a = 0;
for (int i = 0, n = _skeleton->slotsCount; i < n; i++) { //循环所有的 slot
spSlot* slot = _skeleton->drawOrder[i]; // 按 slot 的 zorder 逐个遍历渲染
if (!slot->attachment) continue;
Texture2D *texture = nullptr;
switch (slot->attachment->type) { // 根据 slot 上 附件的类型生成渲染的 各个参数
case SP_ATTACHMENT_REGION: {
spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
texture = getTexture(attachment);
uvs = attachment->uvs;
verticesCount = 8;
triangles = quadTriangles;
trianglesCount = 6;
r = attachment->r;
g = attachment->g;
b = attachment->b;
a = attachment->a;
break;
}
case SP_ATTACHMENT_MESH: {
spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment;
spMeshAttachment_computeWorldVertices(attachment, slot, _worldVertices);
texture = getTexture(attachment);
uvs = attachment->uvs;
verticesCount = attachment->verticesCount;
triangles = attachment->triangles;
trianglesCount = attachment->trianglesCount;
r = attachment->r;
g = attachment->g;
b = attachment->b;
a = attachment->a;
break;
}
case SP_ATTACHMENT_SKINNED_MESH: {
spSkinnedMeshAttachment* attachment = (spSkinnedMeshAttachment*)slot->attachment;
spSkinnedMeshAttachment_computeWorldVertices(attachment, slot, _worldVertices);
texture = getTexture(attachment);
uvs = attachment->uvs;
verticesCount = attachment->uvsCount;
triangles = attachment->triangles;
trianglesCount = attachment->trianglesCount;
r = attachment->r;
g = attachment->g;
b = attachment->b;
a = attachment->a;
break;
}
default: ;
}
if (texture) //有渲染图片才需要渲染
{
if (slot->data->blendMode != blendMode) // 对比当前slot 的渲染 blendMode 是否有所变化,如果有所变化就将之前的渲染命令先执行
{
_batch->flush();
blendMode = slot->data->blendMode;
switch (slot->data->blendMode) {
case SP_BLEND_MODE_ADDITIVE:
GL::blendFunc(_premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE);
break;
case SP_BLEND_MODE_MULTIPLY:
GL::blendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
break;
case SP_BLEND_MODE_SCREEN:
GL::blendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
break;
default:
GL::blendFunc(_blendFunc.src, _blendFunc.dst);
}
}
color.a = _skeleton->a * slot->a * a * 255;
float multiplier = _premultipliedAlpha ? color.a : 255;
color.r = _skeleton->r * slot->r * r * multiplier;
color.g = _skeleton->g * slot->g * g * multiplier;
color.b = _skeleton->b * slot->b * b * multiplier;
_batch->add(texture, _worldVertices, uvs, verticesCount, triangles, trianglesCount, &color);
}
}
_batch->flush();
// debug 相关的绘制
if (_debugSlots || _debugBones) {
Director* director = Director::getInstance();
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);
if (_debugSlots) {
// Slots.
DrawPrimitives::setDrawColor4B(0, 0, 255, 255);
glLineWidth(1);
Vec2 points[4];
V3F_C4B_T2F_Quad quad;
for (int i = 0, n = _skeleton->slotsCount; i < n; i++) {
spSlot* slot = _skeleton->drawOrder[i];
if (!slot->attachment || slot->attachment->type != SP_ATTACHMENT_REGION) continue;
spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
points[0] = Vec2(_worldVertices[0], _worldVertices[1]);
points[1] = Vec2(_worldVertices[2], _worldVertices[3]);
points[2] = Vec2(_worldVertices[4], _worldVertices[5]);
points[3] = Vec2(_worldVertices[6], _worldVertices[7]);
DrawPrimitives::drawPoly(points, 4, true);
}
}
if (_debugBones) {
// Bone lengths.
glLineWidth(2);
DrawPrimitives::setDrawColor4B(255, 0, 0, 255);
for (int i = 0, n = _skeleton->bonesCount; i < n; i++) {
spBone *bone = _skeleton->bones[i];
float x = bone->data->length * bone->m00 + bone->worldX;
float y = bone->data->length * bone->m10 + bone->worldY;
DrawPrimitives::drawLine(Vec2(bone->worldX, bone->worldY), Vec2(x, y));
}
// Bone origins.
DrawPrimitives::setPointSize(4);
DrawPrimitives::setDrawColor4B(0, 0, 255, 255); // Root bone is blue.
for (int i = 0, n = _skeleton->bonesCount; i < n; i++) {
spBone *bone = _skeleton->bones[i];
DrawPrimitives::drawPoint(Vec2(bone->worldX, bone->worldY));
if (i == 0) DrawPrimitives::setDrawColor4B(0, 255, 0, 255);
}
}
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
}
void PolygonBatch::add (const Texture2D* addTexture,
const float* addVertices, const float* uvs, int addVerticesCount,
const int* addTriangles, int addTrianglesCount,
Color4B* color) {
if (
addTexture != _texture
|| _verticesCount + (addVerticesCount >> 1) > _capacity
|| _trianglesCount + addTrianglesCount > _capacity * 3) {
this->flush();
_texture = addTexture;
}
for (int i = 0; i < addTrianglesCount; ++i, ++_trianglesCount)
_triangles[_trianglesCount] = addTriangles[i] + _verticesCount;
for (int i = 0; i < addVerticesCount; i += 2, ++_verticesCount) {
V2F_C4B_T2F* vertex = _vertices + _verticesCount;
vertex->vertices.x = addVertices[i];
vertex->vertices.y = addVertices[i + 1];
vertex->colors = *color;
vertex->texCoords.u = uvs[i];
vertex->texCoords.v = uvs[i + 1];
}
}
void PolygonBatch::flush () {
if (!_verticesCount) return;
GL::bindTexture2D(_texture->getName());
GL::bindVAO(0);
glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION);
glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_COLOR);
glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_TEX_COORDS);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(V2F_C4B_T2F), &_vertices[0].vertices);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V2F_C4B_T2F), &_vertices[0].colors);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, sizeof(V2F_C4B_T2F), &_vertices[0].texCoords);
glDrawElements(GL_TRIANGLES, _trianglesCount, GL_UNSIGNED_SHORT, _triangles);
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, _verticesCount);
_verticesCount = 0;
_trianglesCount = 0;
CHECK_GL_ERROR_DEBUG();
}
从上面的代码,可以看出spine的渲染过程其实很简单,就是按照 slot 的zorder 将渲染的 数据加入一个批处理对象 PolygonBatch 中统一执行,只有遇到渲染的图片不在一张纹理上,或者渲染的BlendModel 不同才会分批渲染。从这份代码可以看出spine 的渲染效率是相当的高的。在极限情况下(所有资源都在一张合图上,所有slot的 blendModel都一样的情况下)spine的渲染只需要一次。
重回正题,如果我们需要将插入动画slot上的node能够保持原有的zorder必然就只需要在 slot 上插入了node的地方先把之前的渲染好在渲染node在继续渲染就能达到目的。下面就开始动手改代码。
2、实现代码:
1、给对象增加实现接口
/** Draws a skeleton. */
class SkeletonRenderer: public cocos2d::Node, public cocos2d::BlendProtocol
{
private:
<span style="white-space:pre"> </span>std::vector<cocos2d::Node*><span style="white-space:pre"> </span>m_slotNodes;
public:
<span style="white-space:pre"> </span>cocos2d::Node* getNodeForSlot(const char* slotName);
private:
<span style="white-space:pre"> </span>void<span style="white-space:pre"> </span>freeSlotNodes();
<span style="white-space:pre"> </span>void<span style="white-space:pre"> </span>drawSlotNode(const cocos2d::Mat4& transform, uint32_t transformFlags, spSlot* slot);
2、接口实现
void SkeletonRenderer::freeSlotNodes()
{
for (auto node : m_slotNodes)
{
node->release();
}
}
cocos2d::Node* SkeletonRenderer::getNodeForSlot(const char* slotName)
{
spSlot* slot = findSlot(slotName);
if (slot != NULL)
{
cocos2d::Node* node = cocos2d::Node::create();
if (node != nullptr)
{
node->setPosition(0, 0);
slot->node = node;
node->retain();
m_slotNodes.push_back(node);
}
return node;
}
return nullptr;
}
void SkeletonRenderer::drawSlotNode(const cocos2d::Mat4& transform, uint32_t transformFlags, spSlot* slot)
{
if (slot->node == NULL) return;
_batch->flush();
int blendMode = -1;
cocos2d::Node* pNode = (cocos2d::Node*)slot->node;
blendMode = slot->data->blendMode;
cocos2d::BlendFunc func;
switch (slot->data->blendMode) {
case SP_BLEND_MODE_ADDITIVE:
func.src = _premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
func.dst = GL_ONE;
break;
case SP_BLEND_MODE_MULTIPLY:
func.src = GL_DST_COLOR;
func.dst = GL_ONE_MINUS_SRC_ALPHA;
break;
case SP_BLEND_MODE_SCREEN:
func.src = GL_ONE;
func.dst = GL_ONE_MINUS_SRC_COLOR;
break;
default:
func = _blendFunc;
}
setNodeBlendFunc(pNode, func);
setEnableRecursiveCascadingRGBA(pNode, true);
spBone* bone = slot->bone;
if (bone != NULL){
pNode->setPosition(ccp(bone->worldX, bone->worldY));
pNode->setRotation(-bone->worldRotation);
pNode->setScaleX(bone->worldScaleX);
pNode->setScaleY(bone->worldScaleY);
}
pNode->setOpacity(255 * slot->a);
pNode->setColor(ccc3(255 * slot->r, 255 * slot->g, 255 * slot->b));
m_drawRender->clean();
m_drawRender->clearDrawStats();
pNode->visit(m_drawRender, transform, transformFlags);
m_drawRender->render();
getGLProgramState()->apply(transform); //最重要的一步,由于渲染node的时候会改变当前的shader,所以最后需要重新恢复。
}
static void setNodeBlendFunc(cocos2d::Node* node, cocos2d::BlendFunc func)
{
BlendProtocol *ptr = dynamic_cast<BlendProtocol*>(node);
if (ptr != nullptr)
{
ptr->setBlendFunc(func);
}
for (auto child : node->getChildren())
{
setNodeBlendFunc(child, func);
}
}
static void setEnableRecursiveCascadingRGBA(Node* node, bool enable)
{
CCRGBAProtocol* rgba = dynamic_cast<CCRGBAProtocol*>(node);
if (rgba)
{
rgba->setCascadeColorEnabled(enable);
rgba->setCascadeOpacityEnabled(enable);
}
CCObject* obj;
Vector<Node*> children = node->getChildren();
Vector<Node*>::iterator it;
for (it = children.begin(); it != children.end(); it++)
{
Node* child = *it;
setEnableRecursiveCascadingRGBA(child, enable);
}
}
3、修改spine的渲染函数
void SkeletonRenderer::drawSkeleton (const Mat4 &transform, uint32_t transformFlags) {
getGLProgramState()->apply(transform);
Color3B nodeColor = getColor();
_skeleton->r = nodeColor.r / (float)255;
_skeleton->g = nodeColor.g / (float)255;
_skeleton->b = nodeColor.b / (float)255;
_skeleton->a = getDisplayedOpacity() / (float)255;
int blendMode = -1;
Color4B color;
const float* uvs = nullptr;
int verticesCount = 0;
const int* triangles = nullptr;
int trianglesCount = 0;
float r = 0, g = 0, b = 0, a = 0;
for (int i = 0, n = _skeleton->slotsCount; i < n; i++) {
spSlot* slot = _skeleton->drawOrder[i];
if (!slot->attachment) continue;
Texture2D *texture = nullptr;
switch (slot->attachment->type) {
case SP_ATTACHMENT_REGION: {
spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
texture = getTexture(attachment);
uvs = attachment->uvs;
verticesCount = 8;
triangles = quadTriangles;
trianglesCount = 6;
r = attachment->r;
g = attachment->g;
b = attachment->b;
a = attachment->a;
break;
}
case SP_ATTACHMENT_MESH: {
spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment;
spMeshAttachment_computeWorldVertices(attachment, slot, _worldVertices);
texture = getTexture(attachment);
uvs = attachment->uvs;
verticesCount = attachment->verticesCount;
triangles = attachment->triangles;
trianglesCount = attachment->trianglesCount;
r = attachment->r;
g = attachment->g;
b = attachment->b;
a = attachment->a;
break;
}
case SP_ATTACHMENT_SKINNED_MESH: {
spSkinnedMeshAttachment* attachment = (spSkinnedMeshAttachment*)slot->attachment;
spSkinnedMeshAttachment_computeWorldVertices(attachment, slot, _worldVertices);
texture = getTexture(attachment);
uvs = attachment->uvs;
verticesCount = attachment->uvsCount;
triangles = attachment->triangles;
trianglesCount = attachment->trianglesCount;
r = attachment->r;
g = attachment->g;
b = attachment->b;
a = attachment->a;
break;
}
default: ;
}
if (texture)
{
if (slot->data->blendMode != blendMode)
{
_batch->flush();
blendMode = slot->data->blendMode;
switch (slot->data->blendMode) {
case SP_BLEND_MODE_ADDITIVE:
GL::blendFunc(_premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE);
break;
case SP_BLEND_MODE_MULTIPLY:
GL::blendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
break;
case SP_BLEND_MODE_SCREEN:
GL::blendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
break;
default:
GL::blendFunc(_blendFunc.src, _blendFunc.dst);
}
}
color.a = _skeleton->a * slot->a * a * 255;
float multiplier = _premultipliedAlpha ? color.a : 255;
color.r = _skeleton->r * slot->r * r * multiplier;
color.g = _skeleton->g * slot->g * g * multiplier;
color.b = _skeleton->b * slot->b * b * multiplier;
_batch->add(texture, _worldVertices, uvs, verticesCount, triangles, trianglesCount, &color);
}
<span style="color:#ff0000;"> drawSlotNode(transform, transformFlags, slot);</span>
}
_batch->flush();
if (_debugSlots || _debugBones) {
Director* director = Director::getInstance();
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);
if (_debugSlots) {
// Slots.
DrawPrimitives::setDrawColor4B(0, 0, 255, 255);
glLineWidth(1);
Vec2 points[4];
V3F_C4B_T2F_Quad quad;
for (int i = 0, n = _skeleton->slotsCount; i < n; i++) {
spSlot* slot = _skeleton->drawOrder[i];
if (!slot->attachment || slot->attachment->type != SP_ATTACHMENT_REGION) continue;
spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
points[0] = Vec2(_worldVertices[0], _worldVertices[1]);
points[1] = Vec2(_worldVertices[2], _worldVertices[3]);
points[2] = Vec2(_worldVertices[4], _worldVertices[5]);
points[3] = Vec2(_worldVertices[6], _worldVertices[7]);
DrawPrimitives::drawPoly(points, 4, true);
}
}
if (_debugBones) {
// Bone lengths.
glLineWidth(2);
DrawPrimitives::setDrawColor4B(255, 0, 0, 255);
for (int i = 0, n = _skeleton->bonesCount; i < n; i++) {
spBone *bone = _skeleton->bones[i];
float x = bone->data->length * bone->m00 + bone->worldX;
float y = bone->data->length * bone->m10 + bone->worldY;
DrawPrimitives::drawLine(Vec2(bone->worldX, bone->worldY), Vec2(x, y));
}
// Bone origins.
DrawPrimitives::setPointSize(4);
DrawPrimitives::setDrawColor4B(0, 0, 255, 255); // Root bone is blue.
for (int i = 0, n = _skeleton->bonesCount; i < n; i++) {
spBone *bone = _skeleton->bones[i];
DrawPrimitives::drawPoint(Vec2(bone->worldX, bone->worldY));
if (i == 0) DrawPrimitives::setDrawColor4B(0, 255, 0, 255);
}
}
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
}