Spine Slot 增加 绑定 节点且能保持 与slot的zoder一致

本文介绍如何在 Cocos2dx 的 Spine 动画中为特定 Slot 添加自定义 Node,同时保持原有 Z 轴顺序。通过修改 Spine 渲染流程,在每个 Slot 渲染前后插入自定义内容,实现了动画中添加额外元素的需求。
摘要由CSDN通过智能技术生成

 前言:

在写本文之前曾在网上找过 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);
	}
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值