cocos2dx 3.10版本 用图片对spine局部换肤,不需要改源码

前几天项目要求spine用外部文件换肤,然后看了一下别人的教程大概知道spine的原理后写了这个类,直接放到项目中就可以用。

其原理大概就是
mySpine
重写drawSkeleton类,在渲染时找出需要换肤slot,用外部的texture来渲染

有三种类型的渲染方式
SP_ATTACHMENT_REGION:是一个矩形图片的渲染,所以直接用类中mUsv来设置uvs
SP_ATTACHMENT_MESH:usv需要计算一下,大概就是计算一下 x = (x-minx)/(maxx-minx), y = (y-miny)/(maxy-miny)
SP_ATTACHMENT_SKINNED_MESH:跟SP_ATTACHMENT_MESH差不多,只是SP_ATTACHMENT_MESH用verticesCount,当前用uvsCount

直接贴出代码
mySpine.h

#pragma once
#include "cocos2d.h"
#include <spine/spine-cocos2dx.h>
#include "cocos2d\cocos\editor-support\spine\PolygonBatch.h"

using namespace std;
using namespace cocos2d;
struct spSkeleton;

/*

mySpine
	重写drawSkeleton类,在渲染时找出需要换肤slot,用外部的texture来渲染

	有三种类型的渲染方式
	SP_ATTACHMENT_REGION:是一个矩形图片的渲染,所以直接用类中mUsv来设置uvs
	SP_ATTACHMENT_MESH:usv需要计算一下,大概就是计算一下 x = (x-minx)/(maxx-minx), y = (y-miny)/(maxy-miny)
	SP_ATTACHMENT_SKINNED_MESH:跟SP_ATTACHMENT_MESH差不多,只是SP_ATTACHMENT_MESH用verticesCount,当前用uvsCount
*/

class mySpine :public spine::SkeletonAnimation {
public:
	~mySpine() { if (mFloat)delete[]mFloat; }
	mySpine(const std::string& skeletonDataFile, const std::string& atlasFile);
	static mySpine * create(const std::string& skeletonDataFile, const std::string& atlasFile);

	virtual void drawSkeleton(const Mat4 &transform, uint32_t transformFlags);
	void setSkinFile(string slotName, string filePath);

private:

	Map<string, Sprite*>useSkin;	//保存需要换肤的slot
	float * mFloat;				//用于SP_ATTACHMENT_MESH和SP_ATTACHMENT_SKINNED_MESH渲染中保存计算出新的usv的值
	float mUsv[8] = { 0.0f,1.0f,0.0f,0.0f,1.0f,0.0f,1.0f,1.0f }; //用于SP_ATTACHMENT_REGION的渲染
};

mySpine.cpp

#include "mySpine.h"

mySpine::mySpine(const std::string & skeletonDataFile, const std::string & atlasFile)
	:SkeletonAnimation(skeletonDataFile, atlasFile, 1.0f)
{
	mFloat = NULL;
	initialize();
}

mySpine * mySpine::create(const std::string& skeletonDataFile, const std::string& atlasFile)
{
	mySpine* node = new mySpine(skeletonDataFile, atlasFile);
	node->autorelease();
	return node;
}

inline void mySpine::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 = NULL;
		if (useSkin.at(slot->data->name))
		{
			texture = useSkin.at(slot->data->name)->getTexture();
		}

		switch (slot->attachment->type) {
		case SP_ATTACHMENT_REGION: {
			const int quadTriangles[6] = { 0, 1, 2, 2, 3, 0 };
			spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
			spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
			if (texture)
			{
				uvs = mUsv;
			}
			else {
				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);

			if (texture) {
				if (mFloat)
				{
					delete[] mFloat;
				}
				mFloat = new float[attachment->verticesCount];

				float minX = 1.0, maxX = 0.0, minY = 1.0, maxY = 0.0;
				for (int i = 0; i < attachment->verticesCount; i++)
				{
					float n = attachment->uvs[i];
					if (i % 2 == 0)
					{
						if (n < minX) { minX = n; }
						if (n > maxX) { maxX = n; }
					}
					else {
						if (n < minY) { minY = n; }
						if (n > maxY) { maxY = n; }
					}
					mFloat[i] = n;
				}
				float lenx = maxX - minX;
				float leny = maxY - minY;
				for (int i = 0; i < attachment->verticesCount; i++)
				{
					if (i % 2 == 0)
					{
						mFloat[i] -= minX;
						mFloat[i] /= lenx;
					}
					else {
						mFloat[i] -= minY;
						mFloat[i] /= leny;
					}
				}
				uvs = mFloat;
			}
			else {
				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);

			if (!texture)
			{
				texture = getTexture(attachment);
				uvs = attachment->uvs;
			}
			else {
				if (mFloat)
				{
					delete[] mFloat;
				}
				mFloat = new float[attachment->uvsCount];

				float minX = 1.0, maxX = 0.0, minY = 1.0, maxY = 0.0;
				for (int i = 0; i < attachment->uvsCount; i++)
				{
					float n = attachment->uvs[i];
					if (i % 2 == 0)
					{
						if (n < minX) { minX = n; }
						if (n > maxX) { maxX = n; }
					}
					else {
						if (n < minY) { minY = n; }
						if (n > maxY) { maxY = n; }
					}
					mFloat[i] = n;
				}
				float lenx = maxX - minX;
				float leny = maxY - minY;
				for (int i = 0; i < attachment->uvsCount; i++)
				{
					if (i % 2 == 0)
					{
						mFloat[i] -= minX;
						mFloat[i] /= lenx;
					}
					else {
						mFloat[i] -= minY;
						mFloat[i] /= leny;
					}
				}
				uvs = mFloat;
			}

			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);
		}
	}
	_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);
	}
}

void mySpine::setSkinFile(string slot, string file)
{
	useSkin.insert(slot, Sprite::create(file));
}

=.=大神现在cocos2d3.15还没发布的支持spine最新版里的绘制代码不同了- -没法用了。。

楼主,getTexture(attachment) 怎么没有这函数?

最新的spine的版本还是基于3.10

cpp里面 ////////////////////////括住的内容就是重写draw新增的内容

mySpine.h

#include <\spine/spine-cocos2dx.h>
#include "cocos2d\cocos\editor-support\spine\SkeletonBatch.h"
#include "cocos2d\cocos\editor-support\spine\AttachmentVertices.h"

using namespace std;
using namespace cocos2d;
using namespace spine;
struct spSkeleton;

class mySpine :public spine::SkeletonAnimation {
public:
	~mySpine() {}
	mySpine();
	static mySpine * create(const std::string& skeletonDataFile, const std::string& atlasFile);

	virtual void draw(Renderer* renderer, const Mat4& transform, uint32_t transformFlags);

	void setSkinFile(string slot, string file);
	Map<string, Sprite*>useSkin;
	AttachmentVertices* att;
};

mySpine.cpp

#include "mySpine.h"

mySpine::mySpine()
	:SkeletonAnimation()
{

}

mySpine * mySpine::create(const std::string& skeletonDataFile, const std::string& atlasFile)
{
	mySpine* node = new mySpine();
	spAtlas* atlas = spAtlas_createFromFile(atlasFile.c_str(), 0);
	node->initWithJsonFile(skeletonDataFile, atlas, 1.0);
	node->autorelease();
	return node;
}

void mySpine::draw(Renderer * renderer, const Mat4 & transform, uint32_t transformFlags)
{
	SkeletonBatch* batch = SkeletonBatch::getInstance();

	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;

	Color4F color;
	AttachmentVertices* attachmentVertices = nullptr;
	for (int i = 0, n = _skeleton->slotsCount; i < n; ++i) {
		spSlot* slot = _skeleton->drawOrder[i];
		if (!slot->attachment) continue;

		switch (slot->attachment->type) {
		case SP_ATTACHMENT_REGION: {
			spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
			spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
			attachmentVertices = getAttachmentVertices(attachment);
			color.r = attachment->r;
			color.g = attachment->g;
			color.b = attachment->b;
			color.a = attachment->a;
			break;
		}
		case SP_ATTACHMENT_MESH: {
			spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment;
			spMeshAttachment_computeWorldVertices(attachment, slot, _worldVertices);
			attachmentVertices = getAttachmentVertices(attachment);
			color.r = attachment->r;
			color.g = attachment->g;
			color.b = attachment->b;
			color.a = attachment->a;
			break;
		}
		default:
			continue;
		}

		color.a *= _skeleton->a * slot->a * 255;
		float multiplier = _premultipliedAlpha ? color.a : 255;
		color.r *= _skeleton->r * slot->r * multiplier;
		color.g *= _skeleton->g * slot->g * multiplier;
		color.b *= _skeleton->b * slot->b * multiplier;

		for (int v = 0, w = 0, vn = attachmentVertices->_triangles->vertCount; v < vn; ++v, w += 2) {
			V3F_C4B_T2F* vertex = attachmentVertices->_triangles->verts + v;
			vertex->vertices.x = _worldVertices[w];
			vertex->vertices.y = _worldVertices[w + 1];
			vertex->colors.r = (GLubyte)color.r;
			vertex->colors.g = (GLubyte)color.g;
			vertex->colors.b = (GLubyte)color.b;
			vertex->colors.a = (GLubyte)color.a;
		}

		BlendFunc blendFunc;
		switch (slot->data->blendMode) {
		case SP_BLEND_MODE_ADDITIVE:
			blendFunc.src = _premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
			blendFunc.dst = GL_ONE;
			break;
		case SP_BLEND_MODE_MULTIPLY:
			blendFunc.src = GL_DST_COLOR;
			blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
			break;
		case SP_BLEND_MODE_SCREEN:
			blendFunc.src = GL_ONE;
			blendFunc.dst = GL_ONE_MINUS_SRC_COLOR;
			break;
		default:
			blendFunc.src = _premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
			blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
		}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool isFind = false;
		if (useSkin.at(slot->data->name))
		{
			isFind = true;
			useSkin.at(slot->data->name)->getTexture();
		}

		if (isFind)
		{
			switch (slot->attachment->type) {
			case SP_ATTACHMENT_REGION: {
				attachmentVertices->_triangles->verts[0].texCoords.u = 0.0;
				attachmentVertices->_triangles->verts[0].texCoords.v = 1.0;
				attachmentVertices->_triangles->verts[1].texCoords.u = 0.0;
				attachmentVertices->_triangles->verts[1].texCoords.v = 0.0;
				attachmentVertices->_triangles->verts[2].texCoords.u = 1.0;
				attachmentVertices->_triangles->verts[2].texCoords.v = 0.0;
				attachmentVertices->_triangles->verts[3].texCoords.u = 1.0;
				attachmentVertices->_triangles->verts[3].texCoords.v = 1.0;
			}break;
			case SP_ATTACHMENT_MESH: {
				float minX = 1.0, maxX = 0.0, minY = 1.0, maxY = 0.0;
				for (int i = 0; i < attachmentVertices->_triangles->vertCount; i++)
				{
					Tex2F t2f = attachmentVertices->_triangles->verts[i].texCoords;
					if (t2f.u < minX) { minX = t2f.u; }
					if (t2f.u > maxX) { maxX = t2f.u; }
					if (t2f.v < minY) { minY = t2f.v; }
					if (t2f.v > maxY) { maxY = t2f.v; }
				}
				float lenx = maxX - minX;
				float leny = maxY - minY;
				for (int i = 0; i < attachmentVertices->_triangles->vertCount; i++)
				{
					Tex2F t2f = attachmentVertices->_triangles->verts[i].texCoords;
					attachmentVertices->_triangles->verts[i].texCoords.u -= minX;
					attachmentVertices->_triangles->verts[i].texCoords.u /= lenx;

					attachmentVertices->_triangles->verts[i].texCoords.v -= minY;
					attachmentVertices->_triangles->verts[i].texCoords.v /= leny;
				}
			}break;
			}

			batch->addCommand(renderer, _globalZOrder, useSkin.at(slot->data->name)->getTexture()->getName(), _glProgramState, blendFunc,
				*attachmentVertices->_triangles, transform, transformFlags);
		}
		else {
////////////////////////////////////////////////////////////////////////////////////////////////////////////
			batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc,
				*attachmentVertices->_triangles, transform, transformFlags);
		}
	}

	if (_debugSlots || _debugBones) {
		drawDebug(renderer, transform, transformFlags);
	}
}

void mySpine::setSkinFile(string slot, string file)
{
	useSkin.insert(slot, Sprite::create(file));
}

您好,原先的皮肤如何换回去呢?

你好,你的cocos2dx 3.15的 spine动态换肤OK了么?