[c] Implemented two color tinting. We use our own batching/shader, see SkeletonTwoColorBatch. Currently, every two color tinted skeleton is its own batch. Enable two color tinting by SkeletonRenderer::setTwoColorTint(true) for a specific skeleton instance

This commit is contained in:
badlogic 2017-02-28 15:36:04 +01:00
parent be9b522237
commit 1be3c1a6b4
9 changed files with 633 additions and 35 deletions

View File

@ -187,6 +187,7 @@ static void readColor (_dataInput* input, float *r, float *g, float *b, float *a
#define SLOT_ATTACHMENT 0
#define SLOT_COLOR 1
#define SLOT_TWO_COLOR 2
#define PATH_POSITION 0
#define PATH_SPACING 1
@ -261,6 +262,20 @@ static spAnimation* _spSkeletonBinary_readAnimation (spSkeletonBinary* self, con
unsigned char timelineType = readByte(input);
int frameCount = readVarint(input, 1);
switch (timelineType) {
case SLOT_ATTACHMENT: {
spAttachmentTimeline* timeline = spAttachmentTimeline_create(frameCount);
timeline->slotIndex = slotIndex;
for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
const char* attachmentName = readString(input);
/* TODO Avoid copying of attachmentName inside */
spAttachmentTimeline_setFrame(timeline, frameIndex, time, attachmentName);
FREE(attachmentName);
}
kv_push(spTimeline*, timelines, SUPER(timeline));
duration = MAX(duration, timeline->frames[frameCount - 1]);
break;
}
case SLOT_COLOR: {
spColorTimeline* timeline = spColorTimeline_create(frameCount);
timeline->slotIndex = slotIndex;
@ -275,18 +290,20 @@ static spAnimation* _spSkeletonBinary_readAnimation (spSkeletonBinary* self, con
duration = MAX(duration, timeline->frames[(frameCount - 1) * COLOR_ENTRIES]);
break;
}
case SLOT_ATTACHMENT: {
spAttachmentTimeline* timeline = spAttachmentTimeline_create(frameCount);
case SLOT_TWO_COLOR: {
spTwoColorTimeline* timeline = spTwoColorTimeline_create(frameCount);
timeline->slotIndex = slotIndex;
for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
float time = readFloat(input);
const char* attachmentName = readString(input);
/* TODO Avoid copying of attachmentName inside */
spAttachmentTimeline_setFrame(timeline, frameIndex, time, attachmentName);
FREE(attachmentName);
float r, g, b, a;
float r2, g2, b2, a2;
readColor(input, &r, &g, &b, &a);
readColor(input, &a2, &r2, &g2, &b2);
spTwoColorTimeline_setFrame(timeline, frameIndex, time, r, g, b, a, r2, g2, b2);
if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
}
kv_push(spTimeline*, timelines, SUPER(timeline));
duration = MAX(duration, timeline->frames[frameCount - 1]);
kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
duration = MAX(duration, timeline->frames[(frameCount - 1) * TWOCOLOR_ENTRIES]);
break;
}
default: {

View File

@ -93,7 +93,7 @@ static float toColor (const char* value, int index) {
char *error;
int color;
if (strlen(value) != 8) return -1;
if (strlen(value) / 2 < index) return -1;
value += index * 2;
digits[0] = *value;
@ -217,8 +217,8 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
timeline->slotIndex = slotIndex;
for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
const char* s = Json_getString(valueMap, "color", 0);
const char* ds = Json_getString(valueMap, "color", 0);
const char* s = Json_getString(valueMap, "light", 0);
const char* ds = Json_getString(valueMap, "dark", 0);
spTwoColorTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2),
toColor(s, 3), toColor(ds, 0), toColor(ds, 1), toColor(ds, 2));
readCurve(valueMap, SUPER(timeline), frameIndex);

View File

@ -46,6 +46,8 @@
521A8E6519F0C34300D177D7 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 521A8E6319F0C34300D177D7 /* Default-736h@3x.png */; };
52B47A471A53D09C004E4C60 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52B47A461A53D09B004E4C60 /* Security.framework */; };
7602C5551D7DAA1300C7C674 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7602C5541D7DAA1300C7C674 /* CoreText.framework */; };
76A45BDE1E64396800745AA1 /* SkeletonTwoColorBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76A45BDC1E64396800745AA1 /* SkeletonTwoColorBatch.cpp */; };
76A45BDF1E64396800745AA1 /* SkeletonTwoColorBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76A45BDC1E64396800745AA1 /* SkeletonTwoColorBatch.cpp */; };
76AAA3C01D180F7C00C54FCB /* AppDelegate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76AAA3B31D180F7C00C54FCB /* AppDelegate.cpp */; };
76AAA3C11D180F7C00C54FCB /* BatchingExample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76AAA3B61D180F7C00C54FCB /* BatchingExample.cpp */; };
76AAA3C21D180F7C00C54FCB /* GoblinsExample.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76AAA3B81D180F7C00C54FCB /* GoblinsExample.cpp */; };
@ -251,6 +253,8 @@
521A8E6319F0C34300D177D7 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = "<group>"; };
52B47A461A53D09B004E4C60 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
7602C5541D7DAA1300C7C674 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/CoreText.framework; sourceTree = DEVELOPER_DIR; };
76A45BDC1E64396800745AA1 /* SkeletonTwoColorBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkeletonTwoColorBatch.cpp; path = ../../src/spine/SkeletonTwoColorBatch.cpp; sourceTree = "<group>"; };
76A45BDD1E64396800745AA1 /* SkeletonTwoColorBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonTwoColorBatch.h; path = ../../src/spine/SkeletonTwoColorBatch.h; sourceTree = "<group>"; };
76AAA3B31D180F7C00C54FCB /* AppDelegate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppDelegate.cpp; sourceTree = "<group>"; };
76AAA3B41D180F7C00C54FCB /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
76AAA3B51D180F7C00C54FCB /* AppMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppMacros.h; sourceTree = "<group>"; };
@ -573,6 +577,8 @@
76AAA3FF1D18102C00C54FCB /* spine-cocos2dx */ = {
isa = PBXGroup;
children = (
76A45BDC1E64396800745AA1 /* SkeletonTwoColorBatch.cpp */,
76A45BDD1E64396800745AA1 /* SkeletonTwoColorBatch.h */,
76AAA4001D18106000C54FCB /* AttachmentVertices.cpp */,
76AAA4011D18106000C54FCB /* AttachmentVertices.h */,
76AAA4021D18106000C54FCB /* Cocos2dAttachmentLoader.cpp */,
@ -775,6 +781,7 @@
503AE10217EB989F00D1A890 /* RootViewController.mm in Sources */,
503AE10117EB989F00D1A890 /* main.m in Sources */,
76F28CCB1DEC7EBB00CDE54D /* Skin.c in Sources */,
76A45BDE1E64396800745AA1 /* SkeletonTwoColorBatch.cpp in Sources */,
76F28CBF1DEC7EBB00CDE54D /* IkConstraintData.c in Sources */,
76F28CC61DEC7EBB00CDE54D /* Skeleton.c in Sources */,
76AAA4101D18106000C54FCB /* SkeletonRenderer.cpp in Sources */,
@ -840,6 +847,7 @@
76AAA4121D18119F00C54FCB /* AttachmentVertices.cpp in Sources */,
76AAA4131D18119F00C54FCB /* AttachmentVertices.h in Sources */,
76AAA4141D18119F00C54FCB /* Cocos2dAttachmentLoader.cpp in Sources */,
76A45BDF1E64396800745AA1 /* SkeletonTwoColorBatch.cpp in Sources */,
76AAA4151D18119F00C54FCB /* Cocos2dAttachmentLoader.h in Sources */,
76AAA4161D18119F00C54FCB /* SkeletonAnimation.cpp in Sources */,
76AAA4171D18119F00C54FCB /* SkeletonAnimation.h in Sources */,

View File

@ -97,11 +97,11 @@ cocos2d::V3F_C4B_T2F* SkeletonBatch::allocateVertices(uint32_t numVertices) {
return vertices;
}
const cocos2d::TrianglesCommand::Triangles& SkeletonBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) {
cocos2d::TrianglesCommand* SkeletonBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) {
TrianglesCommand* command = nextFreeCommand();
command->init(globalOrder, textureID, glProgramState, blendType, triangles, mv, flags);
renderer->addCommand(command);
return command->getTriangles();
return command;
}
void SkeletonBatch::reset() {

View File

@ -46,7 +46,7 @@ namespace spine {
void update (float delta);
cocos2d::V3F_C4B_T2F* allocateVertices(uint32_t numVertices);
const cocos2d::TrianglesCommand::Triangles& addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags);
cocos2d::TrianglesCommand* addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const cocos2d::TrianglesCommand::Triangles& triangles, const cocos2d::Mat4& mv, uint32_t flags);
protected:
SkeletonBatch ();

View File

@ -31,6 +31,7 @@
#include <spine/SkeletonRenderer.h>
#include <spine/extension.h>
#include <spine/SkeletonBatch.h>
#include <spine/SkeletonTwoColorBatch.h>
#include <spine/AttachmentVertices.h>
#include <spine/Cocos2dAttachmentLoader.h>
#include <algorithm>
@ -66,6 +67,7 @@ void SkeletonRenderer::initialize () {
setOpacityModifyRGB(true);
setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));
setTwoColorTint(true);
}
void SkeletonRenderer::setSkeletonData (spSkeletonData *skeletonData, bool ownsSkeletonData) {
@ -177,6 +179,8 @@ void SkeletonRenderer::update (float deltaTime) {
void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t transformFlags) {
SkeletonBatch* batch = SkeletonBatch::getInstance();
SkeletonTwoColorBatch* twoColorBatch = SkeletonTwoColorBatch::getInstance();
bool isTwoColorTint = this->isTwoColorTint();
Color3B nodeColor = getColor();
_skeleton->color.r = nodeColor.r / (float)255;
@ -185,42 +189,91 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
_skeleton->color.a = getDisplayedOpacity() / (float)255;
Color4F color;
Color4F darkColor;
AttachmentVertices* attachmentVertices = nullptr;
TwoColorTrianglesCommand* lastTwoColorTrianglesCommand = nullptr;
for (int i = 0, n = _skeleton->slotsCount; i < n; ++i) {
spSlot* slot = _skeleton->drawOrder[i];
if (!slot->attachment) continue;
cocos2d::TrianglesCommand::Triangles triangles;
TwoColorTriangles trianglesTwoColor;
switch (slot->attachment->type) {
case SP_ATTACHMENT_REGION: {
spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
attachmentVertices = getAttachmentVertices(attachment);
triangles.indices = attachmentVertices->_triangles->indices;
triangles.indexCount = attachmentVertices->_triangles->indexCount;
triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount);
triangles.vertCount = attachmentVertices->_triangles->vertCount;
memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount);
spRegionAttachment_computeWorldVertices(attachment, slot->bone, (float*)triangles.verts, 0, 6);
if (!isTwoColorTint) {
triangles.indices = attachmentVertices->_triangles->indices;
triangles.indexCount = attachmentVertices->_triangles->indexCount;
triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount);
triangles.vertCount = attachmentVertices->_triangles->vertCount;
memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount);
spRegionAttachment_computeWorldVertices(attachment, slot->bone, (float*)triangles.verts, 0, 6);
} else {
trianglesTwoColor.indices = attachmentVertices->_triangles->indices;
trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount;
trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount);
trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount;
for (int i = 0; i < trianglesTwoColor.vertCount; i++) {
trianglesTwoColor.verts[i].texCoords = attachmentVertices->_triangles->verts[i].texCoords;
}
spRegionAttachment_computeWorldVertices(attachment, slot->bone, (float*)trianglesTwoColor.verts, 0, 7);
}
color.r = attachment->color.r;
color.g = attachment->color.g;
color.b = attachment->color.b;
color.a = attachment->color.a;
if (slot->darkColor) {
darkColor.r = slot->darkColor->r * 255;
darkColor.g = slot->darkColor->g * 255;
darkColor.b = slot->darkColor->b * 255;
} else {
darkColor.r = 0;
darkColor.g = 0;
darkColor.b = 0;
}
break;
}
case SP_ATTACHMENT_MESH: {
spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment;
attachmentVertices = getAttachmentVertices(attachment);
triangles.indices = attachmentVertices->_triangles->indices;
triangles.indexCount = attachmentVertices->_triangles->indexCount;
triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount);
triangles.vertCount = attachmentVertices->_triangles->vertCount;
memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount);
spVertexAttachment_computeWorldVertices(SUPER(attachment), slot, 0, triangles.vertCount * sizeof(cocos2d::V3F_C4B_T2F) / 4, (float*)triangles.verts, 0, 6);
if (!isTwoColorTint) {
triangles.indices = attachmentVertices->_triangles->indices;
triangles.indexCount = attachmentVertices->_triangles->indexCount;
triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount);
triangles.vertCount = attachmentVertices->_triangles->vertCount;
memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount);
spVertexAttachment_computeWorldVertices(SUPER(attachment), slot, 0, triangles.vertCount * sizeof(cocos2d::V3F_C4B_T2F) / 4, (float*)triangles.verts, 0, 6);
} else {
trianglesTwoColor.indices = attachmentVertices->_triangles->indices;
trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount;
trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount);
trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount;
for (int i = 0; i < trianglesTwoColor.vertCount; i++) {
trianglesTwoColor.verts[i].texCoords = attachmentVertices->_triangles->verts[i].texCoords;
}
spVertexAttachment_computeWorldVertices(SUPER(attachment), slot, 0, trianglesTwoColor.vertCount * sizeof(V3F_C4B_C4B_T2F) / 4, (float*)trianglesTwoColor.verts, 0, 7);
}
color.r = attachment->color.r;
color.g = attachment->color.g;
color.b = attachment->color.b;
color.a = attachment->color.a;
color.a = attachment->color.a;
if (slot->darkColor) {
darkColor.r = slot->darkColor->r * 255;
darkColor.g = slot->darkColor->g * 255;
darkColor.b = slot->darkColor->b * 255;
} else {
darkColor.r = 0;
darkColor.g = 0;
darkColor.b = 0;
}
break;
}
default:
@ -252,16 +305,34 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
}
const cocos2d::TrianglesCommand::Triangles& batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, triangles, transform, transformFlags);
for (int v = 0, vn = batchedTriangles.vertCount; v < vn; ++v) {
V3F_C4B_T2F* vertex = batchedTriangles.verts + v;
vertex->colors.r = (GLubyte)color.r;
vertex->colors.g = (GLubyte)color.g;
vertex->colors.b = (GLubyte)color.b;
vertex->colors.a = (GLubyte)color.a;
if (!isTwoColorTint) {
cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, triangles, transform, transformFlags);
for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) {
V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
vertex->colors.r = (GLubyte)color.r;
vertex->colors.g = (GLubyte)color.g;
vertex->colors.b = (GLubyte)color.b;
vertex->colors.a = (GLubyte)color.a;
}
} else {
TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags);
for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) {
V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
vertex->color.r = (GLubyte)color.r;
vertex->color.g = (GLubyte)color.g;
vertex->color.b = (GLubyte)color.b;
vertex->color.a = (GLubyte)color.a;
vertex->color2.r = (GLubyte)darkColor.r;
vertex->color2.g = (GLubyte)darkColor.g;
vertex->color2.b = (GLubyte)darkColor.b;
vertex->color2.a = 1;
}
}
}
if (lastTwoColorTrianglesCommand) lastTwoColorTrianglesCommand->setForceFlush(true);
if (_debugSlots || _debugBones) {
drawDebug(renderer, transform, transformFlags);
@ -394,6 +465,17 @@ bool SkeletonRenderer::setAttachment (const std::string& slotName, const std::st
bool SkeletonRenderer::setAttachment (const std::string& slotName, const char* attachmentName) {
return spSkeleton_setAttachment(_skeleton, slotName.c_str(), attachmentName) ? true : false;
}
void SkeletonRenderer::setTwoColorTint(bool enabled) {
if (enabled)
setGLProgramState(SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState());
else
setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));
}
bool SkeletonRenderer::isTwoColorTint() {
return getGLProgramState() == SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState();
}
spSkeleton* SkeletonRenderer::getSkeleton () {
return _skeleton;

View File

@ -91,6 +91,11 @@ public:
bool setAttachment (const std::string& slotName, const std::string& attachmentName);
/* @param attachmentName May be 0 for no attachment. */
bool setAttachment (const std::string& slotName, const char* attachmentName);
/* Enables/disables two color tinting for this instance. May break batching */
void setTwoColorTint(bool enabled);
/* Whether two color tinting is enabled */
bool isTwoColorTint();
// --- BlendProtocol
virtual void setBlendFunc (const cocos2d::BlendFunc& blendFunc)override;

View File

@ -0,0 +1,324 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable, and
* non-transferable license to use, install, execute, and perform the Spine
* Runtimes software and derivative works solely for personal or internal
* use. Without the written permission of Esoteric Software (see Section 2 of
* the Spine Software License Agreement), you may not (a) modify, translate,
* adapt, or develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes or (b) remove,
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
* or other intellectual property or proprietary rights notices on or in the
* Software, including any copy thereof. Redistributions in binary or source
* form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine/SkeletonTwoColorBatch.h>
#include "external/xxhash/xxhash.h"
#include <spine/extension.h>
#include <algorithm>
USING_NS_CC;
#define EVENT_AFTER_DRAW_RESET_POSITION "director_after_draw"
using std::max;
#define INITIAL_SIZE (10000)
#define MAX_VERTICES 64000
#define MAX_INDICES 64000
#define STRINGIFY(A) #A
namespace spine {
TwoColorTrianglesCommand::TwoColorTrianglesCommand()
:_materialID(0)
,_textureID(0)
,_glProgramState(nullptr)
,_glProgram(nullptr)
,_blendType(BlendFunc::DISABLE)
,_alphaTextureID(0) {
_type = RenderCommand::Type::CUSTOM_COMMAND;
func = [this]() { draw(); };
}
void TwoColorTrianglesCommand::init(float globalOrder, GLuint textureID, GLProgramState* glProgramState, BlendFunc blendType, const TwoColorTriangles& triangles, const Mat4& mv, uint32_t flags) {
CCASSERT(glProgramState, "Invalid GLProgramState");
CCASSERT(glProgramState->getVertexAttribsFlags() == 0, "No custom attributes are supported in QuadCommand");
RenderCommand::init(globalOrder, mv, flags);
_triangles = triangles;
if(_triangles.indexCount % 3 != 0) {
int count = _triangles.indexCount;
_triangles.indexCount = count / 3 * 3;
CCLOGERROR("Resize indexCount from %zd to %zd, size must be multiple times of 3", count, _triangles.indexCount);
}
_mv = mv;
if( _textureID != textureID || _blendType.src != blendType.src || _blendType.dst != blendType.dst ||
_glProgramState != glProgramState ||
_glProgram != glProgramState->getGLProgram()) {
_textureID = textureID;
_blendType = blendType;
_glProgramState = glProgramState;
_glProgram = glProgramState->getGLProgram();
generateMaterialID();
}
}
TwoColorTrianglesCommand::~TwoColorTrianglesCommand() {
}
void TwoColorTrianglesCommand::generateMaterialID()
{
// do not batch if using custom uniforms (since we cannot batch) it
if(_glProgramState->getUniformCount() > 0) {
_materialID = Renderer::MATERIAL_ID_DO_NOT_BATCH;
setSkipBatching(true);
}
else {
int glProgram = (int)_glProgram->getProgram();
int intArray[4] = { glProgram, (int)_textureID, (int)_blendType.src, (int)_blendType.dst};
_materialID = XXH32((const void*)intArray, sizeof(intArray), 0);
}
}
void TwoColorTrianglesCommand::useMaterial() const
{
//Set texture
GL::bindTexture2D(_textureID);
if (_alphaTextureID > 0) {
// ANDROID ETC1 ALPHA supports.
GL::bindTexture2DN(1, _alphaTextureID);
}
//set blend mode
GL::blendFunc(_blendType.src, _blendType.dst);
_glProgramState->apply(_mv);
}
void TwoColorTrianglesCommand::draw() {
SkeletonTwoColorBatch::getInstance()->batch(this);
}
const char* TWO_COLOR_TINT_VERTEX_SHADER = STRINGIFY(
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec4 a_color2;
attribute vec2 a_texCoords;
\n#ifdef GL_ES\n
varying lowp vec4 v_light;
varying lowp vec4 v_dark;
varying mediump vec2 v_texCoord;
\n#else\n
varying vec4 v_light;
varying vec4 v_dark;
varying vec2 v_texCoord;
\n#endif\n
void main() {
v_light = a_color;
v_dark = a_color2;
v_texCoord = a_texCoords;
gl_Position = CC_PMatrix * a_position;
}
);
const char* TWO_COLOR_TINT_FRAGMENT_SHADER = STRINGIFY(
\n#ifdef GL_ES\n
precision lowp float;
\n#endif\n
varying vec4 v_light;
varying vec4 v_dark;
varying vec2 v_texCoord;
void main() {
vec4 texColor = texture2D(CC_Texture0, v_texCoord);
float alpha = texColor.a * v_light.a;
gl_FragColor.a = alpha;
gl_FragColor.rgb = (1.0 - texColor.rgb) * v_dark.rgb * alpha + texColor.rgb * v_light.rgb;
}
);
static SkeletonTwoColorBatch* instance = nullptr;
SkeletonTwoColorBatch* SkeletonTwoColorBatch::getInstance () {
if (!instance) instance = new SkeletonTwoColorBatch();
return instance;
}
void SkeletonTwoColorBatch::destroyInstance () {
if (instance) {
delete instance;
instance = nullptr;
}
}
SkeletonTwoColorBatch::SkeletonTwoColorBatch () {
for (unsigned int i = 0; i < INITIAL_SIZE; i++) {
_commandsPool.push_back(new TwoColorTrianglesCommand());
}
reset ();
// callback after drawing is finished so we can clear out the batch state
// for the next frame
Director::getInstance()->getEventDispatcher()->addCustomEventListener(EVENT_AFTER_DRAW_RESET_POSITION, [this](EventCustom* eventCustom){
this->update(0);
});;
_twoColorTintShader = cocos2d::GLProgram::createWithByteArrays(TWO_COLOR_TINT_VERTEX_SHADER, TWO_COLOR_TINT_FRAGMENT_SHADER);
_twoColorTintShaderState = GLProgramState::getOrCreateWithGLProgram(_twoColorTintShader);
_twoColorTintShaderState->retain();
glGenBuffers(1, &_vertexBufferHandle);
_vertexBuffer = new V3F_C4B_C4B_T2F[MAX_VERTICES];
glGenBuffers(1, &_indexBufferHandle);
_indexBuffer = new unsigned short[MAX_INDICES];
_positionAttributeLocation = _twoColorTintShader->getAttribLocation("a_position");
_colorAttributeLocation = _twoColorTintShader->getAttribLocation("a_color");
_color2AttributeLocation = _twoColorTintShader->getAttribLocation("a_color2");
_texCoordsAttributeLocation = _twoColorTintShader->getAttribLocation("a_texCoords");
}
SkeletonTwoColorBatch::~SkeletonTwoColorBatch () {
Director::getInstance()->getEventDispatcher()->removeCustomEventListeners(EVENT_AFTER_DRAW_RESET_POSITION);
for (unsigned int i = 0; i < _commandsPool.size(); i++) {
delete _commandsPool[i];
_commandsPool[i] = nullptr;
}
_twoColorTintShader->release();
delete _vertexBuffer;
delete _indexBuffer;
}
void SkeletonTwoColorBatch::update (float delta) {
printf("Num batches: %i\n", _numBatches);
reset();
}
V3F_C4B_C4B_T2F* SkeletonTwoColorBatch::allocateVertices(uint32_t numVertices) {
if (_vertices.size() - _numVertices < numVertices) {
V3F_C4B_C4B_T2F* oldData = _vertices.data();
_vertices.resize((_vertices.size() + numVertices) * 2 + 1);
V3F_C4B_C4B_T2F* newData = _vertices.data();
for (uint32_t i = 0; i < this->_nextFreeCommand; i++) {
TwoColorTrianglesCommand* command = _commandsPool[i];
TwoColorTriangles& triangles = (TwoColorTriangles&)command->getTriangles();
triangles.verts = newData + (triangles.verts - oldData);
}
}
V3F_C4B_C4B_T2F* vertices = _vertices.data() + _numVertices;
_numVertices += numVertices;
return vertices;
}
TwoColorTrianglesCommand* SkeletonTwoColorBatch::addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags) {
TwoColorTrianglesCommand* command = nextFreeCommand();
command->init(globalOrder, textureID, glProgramState, blendType, triangles, mv, flags);
renderer->addCommand(command);
return command;
}
void SkeletonTwoColorBatch::batch (TwoColorTrianglesCommand* command) {
if (_numVerticesBuffer + command->getTriangles().vertCount >= MAX_VERTICES || _numIndicesBuffer + command->getTriangles().indexCount >= MAX_INDICES) {
flush(_lastCommand);
}
memcpy(_vertexBuffer + _numVerticesBuffer, command->getTriangles().verts, sizeof(V3F_C4B_C4B_T2F) * command->getTriangles().vertCount);
const Mat4& modelView = command->getModelView();
for (int i = _numVerticesBuffer; i < _numVerticesBuffer + command->getTriangles().vertCount; i++) {
modelView.transformPoint(&_vertexBuffer[i].position);
}
unsigned short vertexOffset = (unsigned short)_numVerticesBuffer;
unsigned short* indices = command->getTriangles().indices;
for (int i = 0, j = _numIndicesBuffer; i < command->getTriangles().indexCount; i++, j++) {
_indexBuffer[j] = indices[i] + vertexOffset;
}
_numVerticesBuffer += command->getTriangles().vertCount;
_numIndicesBuffer += command->getTriangles().indexCount;
uint32_t materialID = command->getMaterialID();
if ((_lastCommand && _lastCommand->getMaterialID() != materialID) || command->isForceFlush()) {
flush(_lastCommand);
}
_lastCommand = command;
}
void SkeletonTwoColorBatch::flush (TwoColorTrianglesCommand* materialCommand) {
if (!materialCommand)
return;
materialCommand->useMaterial();
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferHandle);
glBufferData(GL_ARRAY_BUFFER, sizeof(V3F_C4B_C4B_T2F) * _numVerticesBuffer , _vertexBuffer, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(_positionAttributeLocation);
glEnableVertexAttribArray(_colorAttributeLocation);
glEnableVertexAttribArray(_color2AttributeLocation);
glEnableVertexAttribArray(_texCoordsAttributeLocation);
glVertexAttribPointer(_positionAttributeLocation, 3, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_C4B_T2F), (GLvoid*)offsetof(V3F_C4B_C4B_T2F, position));
glVertexAttribPointer(_colorAttributeLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V3F_C4B_C4B_T2F), (GLvoid*)offsetof(V3F_C4B_C4B_T2F, color));
glVertexAttribPointer(_color2AttributeLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V3F_C4B_C4B_T2F), (GLvoid*)offsetof(V3F_C4B_C4B_T2F, color2));
glVertexAttribPointer(_texCoordsAttributeLocation, 2, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_C4B_T2F), (GLvoid*)offsetof(V3F_C4B_C4B_T2F, texCoords));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * _numIndicesBuffer, _indexBuffer, GL_STATIC_DRAW);
glDrawElements(GL_TRIANGLES, (GLsizei)_numIndicesBuffer, GL_UNSIGNED_SHORT, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
_numVerticesBuffer = 0;
_numIndicesBuffer = 0;
_numBatches++;
}
void SkeletonTwoColorBatch::reset() {
_nextFreeCommand = 0;
_numVertices = 0;
_numVerticesBuffer = 0;
_numIndicesBuffer = 0;
_lastCommand = nullptr;
_numBatches = 0;
}
TwoColorTrianglesCommand* SkeletonTwoColorBatch::nextFreeCommand() {
if (_commandsPool.size() <= _nextFreeCommand) {
unsigned int newSize = _commandsPool.size() * 2 + 1;
for (int i = _commandsPool.size(); i < newSize; i++) {
_commandsPool.push_back(new TwoColorTrianglesCommand());
}
}
TwoColorTrianglesCommand* command = _commandsPool[_nextFreeCommand++];
command->setForceFlush(false);
return command;
}
}

View File

@ -0,0 +1,162 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable, and
* non-transferable license to use, install, execute, and perform the Spine
* Runtimes software and derivative works solely for personal or internal
* use. Without the written permission of Esoteric Software (see Section 2 of
* the Spine Software License Agreement), you may not (a) modify, translate,
* adapt, or develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes or (b) remove,
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
* or other intellectual property or proprietary rights notices on or in the
* Software, including any copy thereof. Redistributions in binary or source
* form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef SPINE_SKELETONTWOCOLORBATCH_H_
#define SPINE_SKELETONTWOCOLORBATCH_H_
#include <spine/spine.h>
#include "cocos2d.h"
#include <vector>
namespace spine {
struct V3F_C4B_C4B_T2F {
cocos2d::Vec3 position;
cocos2d::Color4B color;
cocos2d::Color4B color2;
cocos2d::Tex2F texCoords;
};
struct TwoColorTriangles {
V3F_C4B_C4B_T2F* verts;
unsigned short* indices;
int vertCount;
int indexCount;
};
class TwoColorTrianglesCommand : public cocos2d::CustomCommand {
public:
TwoColorTrianglesCommand();
~TwoColorTrianglesCommand();
void init(float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags);
void useMaterial() const;
inline uint32_t getMaterialID() const { return _materialID; }
inline GLuint getTextureID() const { return _textureID; }
inline const TwoColorTriangles& getTriangles() const { return _triangles; }
inline ssize_t getVertexCount() const { return _triangles.vertCount; }
inline ssize_t getIndexCount() const { return _triangles.indexCount; }
inline const V3F_C4B_C4B_T2F* getVertices() const { return _triangles.verts; }
inline const unsigned short* getIndices() const { return _triangles.indices; }
inline cocos2d::GLProgramState* getGLProgramState() const { return _glProgramState; }
inline cocos2d::BlendFunc getBlendType() const { return _blendType; }
inline const cocos2d::Mat4& getModelView() const { return _mv; }
void draw ();
void setForceFlush (bool forceFlush) { _forceFlush = forceFlush; }
bool isForceFlush () { return _forceFlush; };
protected:
void generateMaterialID();
uint32_t _materialID;
GLuint _textureID;
cocos2d::GLProgramState* _glProgramState;
cocos2d::GLProgram* _glProgram;
cocos2d::BlendFunc _blendType;
TwoColorTriangles _triangles;
cocos2d::Mat4 _mv;
GLuint _alphaTextureID;
bool _forceFlush;
};
class SkeletonTwoColorBatch {
public:
static SkeletonTwoColorBatch* getInstance ();
static void destroyInstance ();
void update (float delta);
V3F_C4B_C4B_T2F* allocateVertices(uint32_t numVertices);
TwoColorTrianglesCommand* addCommand(cocos2d::Renderer* renderer, float globalOrder, GLuint textureID, cocos2d::GLProgramState* glProgramState, cocos2d::BlendFunc blendType, const TwoColorTriangles& triangles, const cocos2d::Mat4& mv, uint32_t flags);
cocos2d::GLProgramState* getTwoColorTintProgramState () { return _twoColorTintShaderState; }
void batch (TwoColorTrianglesCommand* command);
void flush (TwoColorTrianglesCommand* materialCommand);
uint32_t getNumBatches () { return _numBatches; };
protected:
SkeletonTwoColorBatch ();
virtual ~SkeletonTwoColorBatch ();
void reset ();
TwoColorTrianglesCommand* nextFreeCommand ();
// pool of commands
std::vector<TwoColorTrianglesCommand*> _commandsPool;
uint32_t _nextFreeCommand;
// pool of vertices
std::vector<V3F_C4B_C4B_T2F> _vertices;
uint32_t _numVertices;
// two color tint shader and state
cocos2d::GLProgram* _twoColorTintShader;
cocos2d::GLProgramState* _twoColorTintShaderState;
// VBO handles & attribute locations
GLuint _vertexBufferHandle;
V3F_C4B_C4B_T2F* _vertexBuffer;
uint32_t _numVerticesBuffer;
GLuint _indexBufferHandle;
uint32_t _numIndicesBuffer;
unsigned short* _indexBuffer;
GLint _positionAttributeLocation;
GLint _colorAttributeLocation;
GLint _color2AttributeLocation;
GLint _texCoordsAttributeLocation;
// last batched command, needed for flushing to set material
TwoColorTrianglesCommand* _lastCommand;
// number of batches in the last frame
uint32_t _numBatches;
};
}
#endif // SPINE_SKELETONTWOCOLORBATCH_H_