From a133221acd6491bb17b2a1f57c70f496f398eb9c Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sat, 2 Apr 2016 15:23:42 +0200 Subject: [PATCH] More efficient cococs2d-x batching. --- .../3/example/Classes/AppDelegate.cpp | 4 +- .../3/example/Classes/BatchingExample.cpp | 35 +++-- .../3/example/Classes/BatchingExample.h | 7 + .../example/proj.win32/spine-cocos2dx.vcxproj | 4 + .../proj.win32/spine-cocos2dx.vcxproj.filters | 12 ++ .../3/src/spine/AttachmentVertices.cpp | 53 +++++++ .../3/src/spine/AttachmentVertices.h | 50 ++++++ .../3/src/spine/Cocos2dAttachmentLoader.c | 118 ++++++++++++++ .../3/src/spine/Cocos2dAttachmentLoader.h | 48 ++++++ spine-cocos2dx/3/src/spine/SkeletonBatch.cpp | 116 +++----------- spine-cocos2dx/3/src/spine/SkeletonBatch.h | 46 ++---- .../3/src/spine/SkeletonRenderer.cpp | 148 ++++++++++-------- spine-cocos2dx/3/src/spine/SkeletonRenderer.h | 9 +- spine-cocos2dx/3/src/spine/spine-cocos2dx.h | 1 + 14 files changed, 439 insertions(+), 212 deletions(-) create mode 100644 spine-cocos2dx/3/src/spine/AttachmentVertices.cpp create mode 100644 spine-cocos2dx/3/src/spine/AttachmentVertices.h create mode 100644 spine-cocos2dx/3/src/spine/Cocos2dAttachmentLoader.c create mode 100644 spine-cocos2dx/3/src/spine/Cocos2dAttachmentLoader.h diff --git a/spine-cocos2dx/3/example/Classes/AppDelegate.cpp b/spine-cocos2dx/3/example/Classes/AppDelegate.cpp index a4e47ed64..252e465d3 100644 --- a/spine-cocos2dx/3/example/Classes/AppDelegate.cpp +++ b/spine-cocos2dx/3/example/Classes/AppDelegate.cpp @@ -98,8 +98,8 @@ bool AppDelegate::applicationDidFinishLaunching () { director->setAnimationInterval(1.0f / 60); // create a scene. it's an autorelease object - auto scene = RaptorExample::scene(); - //auto scene = BatchingExample::scene(); + //auto scene = RaptorExample::scene(); + auto scene = BatchingExample::scene(); // run director->runWithScene(scene); diff --git a/spine-cocos2dx/3/example/Classes/BatchingExample.cpp b/spine-cocos2dx/3/example/Classes/BatchingExample.cpp index ec569d58e..15f936d62 100644 --- a/spine-cocos2dx/3/example/Classes/BatchingExample.cpp +++ b/spine-cocos2dx/3/example/Classes/BatchingExample.cpp @@ -45,28 +45,32 @@ bool BatchingExample::init () { if (!LayerColor::initWithColor(Color4B(128, 128, 128, 255))) return false; // Load the texture atlas. - spAtlas* atlas = spAtlas_createFromFile("spineboy.atlas", 0); - CCASSERT(atlas, "Error reading atlas file."); + _atlas = spAtlas_createFromFile("spineboy.atlas", 0); + CCASSERT(_atlas, "Error reading atlas file."); + + // This attachment loader configures attachments with data needed for cocos2d-x rendering. + // Do not dispose the attachment loader until the skeleton data is disposed! + _attachmentLoader = (spAttachmentLoader*)Cocos2dAttachmentLoader_create(_atlas); // Load the skeleton data. - spSkeletonJson* json = spSkeletonJson_create(atlas); - json->scale = 0.6f; - spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, "spineboy.json"); - CCASSERT(skeletonData, json->error ? json->error : "Error reading skeleton data file."); + spSkeletonJson* json = spSkeletonJson_createWithLoader(_attachmentLoader); + json->scale = 0.6f; // Resizes skeleton data to 60% of the size it was in Spine. + _skeletonData = spSkeletonJson_readSkeletonDataFile(json, "spineboy.json"); + CCASSERT(_skeletonData, json->error ? json->error : "Error reading skeleton data file."); spSkeletonJson_dispose(json); // Setup mix times. - spAnimationStateData* stateData = spAnimationStateData_create(skeletonData); - spAnimationStateData_setMixByName(stateData, "walk", "jump", 0.2f); - spAnimationStateData_setMixByName(stateData, "jump", "run", 0.2f); + _stateData = spAnimationStateData_create(_skeletonData); + spAnimationStateData_setMixByName(_stateData, "walk", "jump", 0.2f); + spAnimationStateData_setMixByName(_stateData, "jump", "run", 0.2f); Size windowSize = Director::getInstance()->getWinSize(); int xMin = (int)(windowSize.width * 0.10f), xMax = (int)windowSize.width - xMin; int yMin = 20, yMax = windowSize.height - 350; for (int i = 0; i < 50; i++) { // Each skeleton node shares the same atlas, skeleton data, and mix times. - SkeletonAnimation* skeletonNode = SkeletonAnimation::createWithData(skeletonData, false); - skeletonNode->setAnimationStateData(stateData); + SkeletonAnimation* skeletonNode = SkeletonAnimation::createWithData(_skeletonData, false); + skeletonNode->setAnimationStateData(_stateData); skeletonNode->setAnimation(0, "walk", true); skeletonNode->addAnimation(0, "jump", false, 3); @@ -90,3 +94,12 @@ bool BatchingExample::init () { return true; } + +BatchingExample::~BatchingExample () { + // SkeletonAnimation instances are cocos2d-x nodes and are disposed of automatically as normal, but the data created + // manually to be shared across multiple SkeletonAnimations needs to be disposed of manually. + spSkeletonData_dispose(_skeletonData); + spAnimationStateData_dispose(_stateData); + spAttachmentLoader_dispose(_attachmentLoader); + spAtlas_dispose(_atlas); +} diff --git a/spine-cocos2dx/3/example/Classes/BatchingExample.h b/spine-cocos2dx/3/example/Classes/BatchingExample.h index 499df60e8..763e786a8 100644 --- a/spine-cocos2dx/3/example/Classes/BatchingExample.h +++ b/spine-cocos2dx/3/example/Classes/BatchingExample.h @@ -40,8 +40,15 @@ public: static cocos2d::Scene* scene (); CREATE_FUNC(BatchingExample); + ~BatchingExample (); virtual bool init (); + +protected: + spAtlas* _atlas; + spAttachmentLoader* _attachmentLoader; + spSkeletonData* _skeletonData; + spAnimationStateData* _stateData; }; #endif // _BATCHINGEXAMPLE_H_ diff --git a/spine-cocos2dx/3/example/proj.win32/spine-cocos2dx.vcxproj b/spine-cocos2dx/3/example/proj.win32/spine-cocos2dx.vcxproj index 2ff10fdda..dd41be986 100644 --- a/spine-cocos2dx/3/example/proj.win32/spine-cocos2dx.vcxproj +++ b/spine-cocos2dx/3/example/proj.win32/spine-cocos2dx.vcxproj @@ -129,6 +129,8 @@ + + @@ -142,6 +144,8 @@ + + diff --git a/spine-cocos2dx/3/example/proj.win32/spine-cocos2dx.vcxproj.filters b/spine-cocos2dx/3/example/proj.win32/spine-cocos2dx.vcxproj.filters index 4851d364d..e0653582d 100644 --- a/spine-cocos2dx/3/example/proj.win32/spine-cocos2dx.vcxproj.filters +++ b/spine-cocos2dx/3/example/proj.win32/spine-cocos2dx.vcxproj.filters @@ -45,6 +45,12 @@ src + + src + + + src + @@ -77,5 +83,11 @@ src + + src + + + src + \ No newline at end of file diff --git a/spine-cocos2dx/3/src/spine/AttachmentVertices.cpp b/spine-cocos2dx/3/src/spine/AttachmentVertices.cpp new file mode 100644 index 000000000..b45320f34 --- /dev/null +++ b/spine-cocos2dx/3/src/spine/AttachmentVertices.cpp @@ -0,0 +1,53 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the + * Software or develop new applications using the Software 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; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) 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 + +USING_NS_CC; + +namespace spine { + +AttachmentVertices::AttachmentVertices (Texture2D* texture, int verticesCount, unsigned short* triangles, int trianglesCount) { + _texture = texture; + + _triangles = new TrianglesCommand::Triangles(); + _triangles->verts = new V3F_C4B_T2F[verticesCount]; + _triangles->vertCount = verticesCount; + _triangles->indices = triangles; + _triangles->indexCount = trianglesCount; +} + +AttachmentVertices::~AttachmentVertices () { + delete [] _triangles->verts; + delete _triangles; +} + +} diff --git a/spine-cocos2dx/3/src/spine/AttachmentVertices.h b/spine-cocos2dx/3/src/spine/AttachmentVertices.h new file mode 100644 index 000000000..72ff827e9 --- /dev/null +++ b/spine-cocos2dx/3/src/spine/AttachmentVertices.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the + * Software or develop new applications using the Software 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; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) 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_ATTACHMENTVERTICES_H_ +#define SPINE_ATTACHMENTVERTICES_H_ + +#include "cocos2d.h" + +namespace spine { + +class AttachmentVertices { +public: + AttachmentVertices (cocos2d::Texture2D* texture, int verticesCount, unsigned short* triangles, int trianglesCount); + virtual ~AttachmentVertices (); + + cocos2d::Texture2D* _texture; + cocos2d::TrianglesCommand::Triangles* _triangles; +}; + +} + +#endif /* SPINE_ATTACHMENTVERTICES_H_ */ diff --git a/spine-cocos2dx/3/src/spine/Cocos2dAttachmentLoader.c b/spine-cocos2dx/3/src/spine/Cocos2dAttachmentLoader.c new file mode 100644 index 000000000..880614c85 --- /dev/null +++ b/spine-cocos2dx/3/src/spine/Cocos2dAttachmentLoader.c @@ -0,0 +1,118 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the + * Software or develop new applications using the Software 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; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) 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 +#include +#include + +USING_NS_CC; +using namespace spine; + +static unsigned short quadTriangles[6] = {0, 1, 2, 2, 3, 0}; + +spAttachment* _Cocos2dAttachmentLoader_createAttachment (spAttachmentLoader* loader, spSkin* skin, spAttachmentType type, + const char* name, const char* path) { + Cocos2dAttachmentLoader* self = SUB_CAST(Cocos2dAttachmentLoader, loader); + return spAttachmentLoader_createAttachment(SUPER(self->atlasAttachmentLoader), skin, type, name, path); +} + +void _Cocos2dAttachmentLoader_configureAttachment (spAttachmentLoader* loader, spAttachment* attachment) { + switch (attachment->type) { + case SP_ATTACHMENT_REGION: { + spRegionAttachment* regionAttachment = SUB_CAST(spRegionAttachment, attachment); + spAtlasRegion* region = (spAtlasRegion*)regionAttachment->rendererObject; + AttachmentVertices* attachmentVertices = new AttachmentVertices((Texture2D*)region->page->rendererObject, 4, quadTriangles, 6); + V3F_C4B_T2F* vertices = attachmentVertices->_triangles->verts; + for (int i = 0, ii = 0; i < 4; ++i, ii += 2) { + vertices[i].texCoords.u = regionAttachment->uvs[ii]; + vertices[i].texCoords.v = regionAttachment->uvs[ii + 1]; + } + regionAttachment->rendererObject = attachmentVertices; + break; + } + case SP_ATTACHMENT_MESH: { + spMeshAttachment* meshAttachment = SUB_CAST(spMeshAttachment, attachment); + spAtlasRegion* region = (spAtlasRegion*)meshAttachment->rendererObject; + AttachmentVertices* attachmentVertices = new AttachmentVertices((Texture2D*)region->page->rendererObject, + meshAttachment->verticesCount >> 1, meshAttachment->triangles, meshAttachment->trianglesCount); + V3F_C4B_T2F* vertices = attachmentVertices->_triangles->verts; + for (int i = 0, ii = 0, nn = meshAttachment->verticesCount; ii < nn; ++i, ii += 2) { + vertices[i].texCoords.u = meshAttachment->uvs[ii]; + vertices[i].texCoords.v = meshAttachment->uvs[ii + 1]; + } + meshAttachment->rendererObject = attachmentVertices; + break; + } + case SP_ATTACHMENT_WEIGHTED_MESH: { + spWeightedMeshAttachment* meshAttachment = SUB_CAST(spWeightedMeshAttachment, attachment); + spAtlasRegion* region = (spAtlasRegion*)meshAttachment->rendererObject; + AttachmentVertices* attachmentVertices = new AttachmentVertices((Texture2D*)region->page->rendererObject, + meshAttachment->uvsCount >> 1, meshAttachment->triangles, meshAttachment->trianglesCount); + V3F_C4B_T2F* vertices = attachmentVertices->_triangles->verts; + for (int i = 0, ii = 0, nn = meshAttachment->uvsCount; ii < nn; ++i, ii += 2) { + vertices[i].texCoords.u = meshAttachment->uvs[ii]; + vertices[i].texCoords.v = meshAttachment->uvs[ii + 1]; + } + meshAttachment->rendererObject = attachmentVertices; + break; + } + default: ; + } +} + +void _Cocos2dAttachmentLoader_disposeAttachment (spAttachmentLoader* loader, spAttachment* attachment) { + switch (attachment->type) { + case SP_ATTACHMENT_REGION: { + spRegionAttachment* regionAttachment = SUB_CAST(spRegionAttachment, attachment); + delete (AttachmentVertices*)regionAttachment->rendererObject; + break; + } + case SP_ATTACHMENT_MESH: { + spMeshAttachment* meshAttachment = SUB_CAST(spMeshAttachment, attachment); + delete (AttachmentVertices*)meshAttachment->rendererObject; + break; + } + case SP_ATTACHMENT_WEIGHTED_MESH: { + spWeightedMeshAttachment* meshAttachment = SUB_CAST(spWeightedMeshAttachment, attachment); + delete (AttachmentVertices*)meshAttachment->rendererObject; + break; + } + default: ; + } +} + +Cocos2dAttachmentLoader* Cocos2dAttachmentLoader_create (spAtlas* atlas) { + Cocos2dAttachmentLoader* self = NEW(Cocos2dAttachmentLoader); + _spAttachmentLoader_init(SUPER(self), _spAttachmentLoader_deinit, _Cocos2dAttachmentLoader_createAttachment, + _Cocos2dAttachmentLoader_configureAttachment, _Cocos2dAttachmentLoader_disposeAttachment); + self->atlasAttachmentLoader = spAtlasAttachmentLoader_create(atlas); + return self; +} diff --git a/spine-cocos2dx/3/src/spine/Cocos2dAttachmentLoader.h b/spine-cocos2dx/3/src/spine/Cocos2dAttachmentLoader.h new file mode 100644 index 000000000..34816348e --- /dev/null +++ b/spine-cocos2dx/3/src/spine/Cocos2dAttachmentLoader.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the + * Software or develop new applications using the Software 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; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) 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_COCOS2DATTACHMENTLOADER_H_ +#define SPINE_COCOS2DATTACHMENTLOADER_H_ + +#include + +extern "C" { + +typedef struct Cocos2dAttachmentLoader { + spAttachmentLoader super; + spAtlasAttachmentLoader* atlasAttachmentLoader; +} Cocos2dAttachmentLoader; + +Cocos2dAttachmentLoader* Cocos2dAttachmentLoader_create (spAtlas* atlas); + +} + +#endif /* SPINE_COCOS2DATTACHMENTLOADER_H_ */ diff --git a/spine-cocos2dx/3/src/spine/SkeletonBatch.cpp b/spine-cocos2dx/3/src/spine/SkeletonBatch.cpp index 6462ad31a..e0428f508 100644 --- a/spine-cocos2dx/3/src/spine/SkeletonBatch.cpp +++ b/spine-cocos2dx/3/src/spine/SkeletonBatch.cpp @@ -34,31 +34,26 @@ #include USING_NS_CC; -using namespace std; namespace spine { static SkeletonBatch* instance = nullptr; -void SkeletonBatch::setCommandSize (int maxVertices, int maxTriangles) { - // 32767 is max index, so 32767 / 3 - (32767 / 3 % 3) = 10920. - CCASSERT(maxTriangles <= 10920, "maxTriangles cannot be > 10920"); - CCASSERT(maxTriangles >= 0, "maxTriangles cannot be < 0"); +void SkeletonBatch::setBufferSize (int vertexCount) { if (instance) delete instance; - instance = new SkeletonBatch(maxVertices, maxTriangles); + instance = new SkeletonBatch(vertexCount); } SkeletonBatch* SkeletonBatch::getInstance () { - if (!instance) instance = new SkeletonBatch(64, 64 * 3); + if (!instance) instance = new SkeletonBatch(8192); return instance; } -SkeletonBatch::SkeletonBatch (int maxVertices, int maxTriangles) : - _maxVertices(maxVertices), _maxTriangles(maxTriangles), - _renderer(nullptr), _transform(nullptr), _transformFlags(0), _globalZOrder(0), _glProgramState(nullptr), - _texture(nullptr), _blendMode(SP_BLEND_MODE_NORMAL) +SkeletonBatch::SkeletonBatch (int capacity) : + _capacity(capacity), _position(0) { - _firstCommand = new Command(maxVertices, maxTriangles); + _buffer = new V3F_C4B_T2F[capacity]; + _firstCommand = new Command(); _command = _firstCommand; Director::getInstance()->getScheduler()->scheduleUpdate(this, -1, false); @@ -73,109 +68,44 @@ SkeletonBatch::~SkeletonBatch () { delete command; command = next; } -} -void SkeletonBatch::setRendererState (Renderer* renderer, const Mat4* transform, uint32_t transformFlags, - float globalZOrder, GLProgramState* glProgramState, bool premultipliedAlpha) { - _renderer = renderer; - _transform = transform; - _transformFlags = transformFlags; - _globalZOrder = globalZOrder; - _glProgramState = glProgramState; - _premultipliedAlpha = premultipliedAlpha; + delete [] _buffer; } void SkeletonBatch::update (float delta) { - // Reuse commands at the beginning of each frame. + _position = 0; _command = _firstCommand; - _command->_triangles->vertCount = 0; - _command->_triangles->indexCount = 0; } -void SkeletonBatch::add (const Texture2D* addTexture, - const float* addVertices, const float* uvs, int addVerticesCount, - const int* addTriangles, int addTrianglesCount, - const Color4B& color, spBlendMode blendMode +void SkeletonBatch::addCommand (cocos2d::Renderer* renderer, float globalZOrder, GLuint textureID, GLProgramState* glProgramState, + BlendFunc blendFunc, const TrianglesCommand::Triangles& triangles, const Mat4& transform, uint32_t transformFlags ) { - if (addTexture != _texture - || blendMode != _blendMode - || _command->_triangles->vertCount + (addVerticesCount >> 1) > _maxVertices - || _command->_triangles->indexCount + addTrianglesCount > _maxTriangles - ) { - this->flush(max(addVerticesCount >> 1, _maxVertices), max(addTrianglesCount, _maxTriangles)); - _texture = addTexture; - _blendMode = blendMode; - } + CCASSERT(_position + triangles.vertCount < _capacity, "SkeletonBatch capacity is too small"); - TrianglesCommand::Triangles* triangles = _command->_triangles; - for (int i = 0; i < addTrianglesCount; ++i, ++triangles->indexCount) - triangles->indices[triangles->indexCount] = addTriangles[i] + triangles->vertCount; + memcpy(_buffer + _position, triangles.verts, sizeof(V3F_C4B_T2F) * triangles.vertCount); + _command->_triangles->verts = _buffer + _position; + _position += triangles.vertCount; - for (int i = 0; i < addVerticesCount; i += 2, ++triangles->vertCount) { - V3F_C4B_T2F* vertex = triangles->verts + triangles->vertCount; - 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]; - } -} + _command->_triangles->vertCount = triangles.vertCount; + _command->_triangles->indexCount = triangles.indexCount; + _command->_triangles->indices = triangles.indices; -void SkeletonBatch::flush (int maxVertices, int maxTriangles) { - if (!_command->_triangles->vertCount) return; + _command->_trianglesCommand->init(globalZOrder, textureID, glProgramState, blendFunc, *_command->_triangles, transform, transformFlags); + renderer->addCommand(_command->_trianglesCommand); - BlendFunc blendFunc; - switch (_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; - } - - _command->_trianglesCommand->init(_globalZOrder, _texture->getName(), _glProgramState, blendFunc, *_command->_triangles, - *_transform, _transformFlags); - _renderer->addCommand(_command->_trianglesCommand); - - if (!_command->_next) _command->_next = new Command(maxVertices, maxTriangles); + if (!_command->_next) _command->_next = new Command(); _command = _command->_next; - - // If not as large as required, insert new command. - if (_command->_maxVertices < maxVertices || _command->_maxTriangles < maxTriangles) { - Command* next = _command->_next; - _command = new Command(maxVertices, maxTriangles); - _command->_next = next; - } - - _command->_triangles->vertCount = 0; - _command->_triangles->indexCount = 0; } -SkeletonBatch::Command::Command (int maxVertices, int maxTriangles) : - _maxVertices(maxVertices), _maxTriangles(maxTriangles), _next(nullptr) +SkeletonBatch::Command::Command () : + _next(nullptr) { _trianglesCommand = new TrianglesCommand(); - _triangles = new TrianglesCommand::Triangles(); - _triangles->verts = new V3F_C4B_T2F[maxVertices]; - _triangles->indices = new GLushort[maxTriangles]; } SkeletonBatch::Command::~Command () { - delete [] _triangles->indices; - delete [] _triangles->verts; delete _triangles; - delete _trianglesCommand; } diff --git a/spine-cocos2dx/3/src/spine/SkeletonBatch.h b/spine-cocos2dx/3/src/spine/SkeletonBatch.h index 19904db87..2c7ae9454 100644 --- a/spine-cocos2dx/3/src/spine/SkeletonBatch.h +++ b/spine-cocos2dx/3/src/spine/SkeletonBatch.h @@ -37,63 +37,39 @@ namespace spine { -/* Batches attachment geometry and issues one or more TrianglesCommands per skeleton. */ -class SkeletonBatch : public cocos2d::Ref { +class SkeletonBatch { public: - /* Sets the default size of each TrianglesCommand. Best to call before getInstance is called for the first time. Default is 64, 192. - * TrianglesCommands may be larger than the specified sizes if required to hold the geometry for a single attachment. */ - static void setCommandSize (int maxVertices, int maxTriangles); + /* Sets the max number of vertices that can be drawn in a single frame. Best to call before getInstance is called for the + * first time. Default is 8192. */ + static void SkeletonBatch::setBufferSize (int vertexCount); static SkeletonBatch* getInstance (); void update (float delta); - void setRendererState (cocos2d::Renderer* renderer, const cocos2d::Mat4* transform, uint32_t transformFlags, - float globalZOrder, cocos2d::GLProgramState* glProgramState, bool premultipliedAlpha); - - void add (const cocos2d::Texture2D* texture, - const float* vertices, const float* uvs, int verticesCount, - const int* triangles, int trianglesCount, - const cocos2d::Color4B& color, spBlendMode blendMode); - - void flush () { - flush(_maxVertices, _maxTriangles); - } + void 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 (int maxVertices, int maxTriangles); + SkeletonBatch (int capacity); virtual ~SkeletonBatch (); - void flush (int maxVertices, int maxTriangles); + cocos2d::V3F_C4B_T2F* _buffer; + int _capacity; + int _position; class Command { public: - Command (int maxVertices, int maxTriangles); + Command (); virtual ~Command (); - int _maxVertices; - int _maxTriangles; cocos2d::TrianglesCommand* _trianglesCommand; cocos2d::TrianglesCommand::Triangles* _triangles; Command* _next; }; - int _maxVertices; - int _maxTriangles; Command* _firstCommand; Command* _command; - - // Renderer state. - cocos2d::Renderer* _renderer; - const cocos2d::Mat4* _transform; - uint32_t _transformFlags; - float _globalZOrder; - cocos2d::GLProgramState* _glProgramState; - bool _premultipliedAlpha; - - // Batch state. - const cocos2d::Texture2D* _texture; - spBlendMode _blendMode; }; } diff --git a/spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp b/spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp index 97a1e0814..572b200ea 100644 --- a/spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp +++ b/spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp @@ -30,9 +30,10 @@ *****************************************************************************/ #include -#include #include #include +#include +#include #include USING_NS_CC; @@ -41,8 +42,6 @@ using std::max; namespace spine { -static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0}; - SkeletonRenderer* SkeletonRenderer::createWithData (spSkeletonData* skeletonData, bool ownsSkeletonData) { SkeletonRenderer* node = new SkeletonRenderer(skeletonData, ownsSkeletonData); node->autorelease(); @@ -62,7 +61,7 @@ SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonD } void SkeletonRenderer::initialize () { - _worldVertices = MALLOC(float, 1000); // Max number of vertices per mesh. + _worldVertices = new float[1000]; // Max number of vertices per mesh. _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED; setOpacityModifyRGB(true); @@ -76,29 +75,30 @@ void SkeletonRenderer::setSkeletonData (spSkeletonData *skeletonData, bool ownsS } SkeletonRenderer::SkeletonRenderer () - : _atlas(0), _debugSlots(false), _debugBones(false), _timeScale(1) { + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _timeScale(1) { } SkeletonRenderer::SkeletonRenderer (spSkeletonData *skeletonData, bool ownsSkeletonData) - : _atlas(0), _debugSlots(false), _debugBones(false), _timeScale(1) { + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _timeScale(1) { initWithData(skeletonData, ownsSkeletonData); } SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, spAtlas* atlas, float scale) - : _atlas(0), _debugSlots(false), _debugBones(false), _timeScale(1) { + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _timeScale(1) { initWithFile(skeletonDataFile, atlas, scale); } SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) - : _atlas(0), _debugSlots(false), _debugBones(false), _timeScale(1) { + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _timeScale(1) { initWithFile(skeletonDataFile, atlasFile, scale); } SkeletonRenderer::~SkeletonRenderer () { if (_ownsSkeletonData) spSkeletonData_dispose(_skeleton->data); - if (_atlas) spAtlas_dispose(_atlas); spSkeleton_dispose(_skeleton); - FREE(_worldVertices); + if (_atlas) spAtlas_dispose(_atlas); + if (_attachmentLoader) spAttachmentLoader_dispose(_attachmentLoader); + delete _worldVertices; } void SkeletonRenderer::initWithData (spSkeletonData* skeletonData, bool ownsSkeletonData) { @@ -108,7 +108,9 @@ void SkeletonRenderer::initWithData (spSkeletonData* skeletonData, bool ownsSkel } void SkeletonRenderer::initWithFile (const std::string& skeletonDataFile, spAtlas* atlas, float scale) { - spSkeletonJson* json = spSkeletonJson_create(atlas); + _attachmentLoader = SUPER(Cocos2dAttachmentLoader_create(_atlas)); + + spSkeletonJson* json = spSkeletonJson_createWithLoader(_attachmentLoader); json->scale = scale; spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, skeletonDataFile.c_str()); CCASSERT(skeletonData, json->error ? json->error : "Error reading skeleton data."); @@ -123,7 +125,9 @@ void SkeletonRenderer::initWithFile (const std::string& skeletonDataFile, const _atlas = spAtlas_createFromFile(atlasFile.c_str(), 0); CCASSERT(_atlas, "Error reading atlas file."); - spSkeletonJson* json = spSkeletonJson_create(_atlas); + _attachmentLoader = SUPER(Cocos2dAttachmentLoader_create(_atlas)); + + spSkeletonJson* json = spSkeletonJson_createWithLoader(_attachmentLoader); json->scale = scale; spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, skeletonDataFile.c_str()); CCASSERT(skeletonData, json->error ? json->error : "Error reading skeleton data file."); @@ -141,7 +145,6 @@ void SkeletonRenderer::update (float deltaTime) { void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t transformFlags) { SkeletonBatch* batch = SkeletonBatch::getInstance(); - batch->setRendererState(renderer, &transform, transformFlags, _globalZOrder, getGLProgramState(), _premultipliedAlpha); Color3B nodeColor = getColor(); _skeleton->r = nodeColor.r / (float)255; @@ -149,72 +152,83 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t _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++) { + int vertexCount = 0; + AttachmentVertices* attachmentVertices = nullptr; + 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; + 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); - 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; + attachmentVertices = getAttachmentVertices(attachment); + color.r = attachment->r; + color.g = attachment->g; + color.b = attachment->b; + color.a = attachment->a; break; } case SP_ATTACHMENT_WEIGHTED_MESH: { spWeightedMeshAttachment* attachment = (spWeightedMeshAttachment*)slot->attachment; spWeightedMeshAttachment_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; + attachmentVertices = getAttachmentVertices(attachment); + color.r = attachment->r; + color.g = attachment->g; + color.b = attachment->b; + color.a = attachment->a; break; } - default: ; - } - if (texture) { - 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, slot->data->blendMode); + 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 = color; + } + + 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; + } + + batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, + *attachmentVertices->_triangles, transform, transformFlags); } - batch->flush(); if (_debugSlots || _debugBones) { _debugCommand.init(_globalZOrder); @@ -236,7 +250,7 @@ void SkeletonRenderer::drawDebug (const Mat4 &transform, uint32_t transformFlags glLineWidth(1); Vec2 points[4]; V3F_C4B_T2F_Quad quad; - for (int i = 0, n = _skeleton->slotsCount; i < n; i++) { + 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; @@ -252,7 +266,7 @@ void SkeletonRenderer::drawDebug (const Mat4 &transform, uint32_t transformFlags // Bone lengths. glLineWidth(2); DrawPrimitives::setDrawColor4B(255, 0, 0, 255); - for (int i = 0, n = _skeleton->bonesCount; i < n; i++) { + for (int i = 0, n = _skeleton->bonesCount; i < n; ++i) { spBone *bone = _skeleton->bones[i]; float x = bone->data->length * bone->a + bone->worldX; float y = bone->data->length * bone->c + bone->worldY; @@ -261,7 +275,7 @@ void SkeletonRenderer::drawDebug (const Mat4 &transform, uint32_t transformFlags // 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++) { + 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); @@ -270,16 +284,16 @@ void SkeletonRenderer::drawDebug (const Mat4 &transform, uint32_t transformFlags director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); } -Texture2D* SkeletonRenderer::getTexture (spRegionAttachment* attachment) const { - return (Texture2D*)((spAtlasRegion*)attachment->rendererObject)->page->rendererObject; +AttachmentVertices* SkeletonRenderer::getAttachmentVertices (spRegionAttachment* attachment) const { + return (AttachmentVertices*)attachment->rendererObject; } -Texture2D* SkeletonRenderer::getTexture (spMeshAttachment* attachment) const { - return (Texture2D*)((spAtlasRegion*)attachment->rendererObject)->page->rendererObject; +AttachmentVertices* SkeletonRenderer::getAttachmentVertices (spMeshAttachment* attachment) const { + return (AttachmentVertices*)attachment->rendererObject; } -Texture2D* SkeletonRenderer::getTexture (spWeightedMeshAttachment* attachment) const { - return (Texture2D*)((spAtlasRegion*)attachment->rendererObject)->page->rendererObject; +AttachmentVertices* SkeletonRenderer::getAttachmentVertices (spWeightedMeshAttachment* attachment) const { + return (AttachmentVertices*)attachment->rendererObject; } Rect SkeletonRenderer::getBoundingBox () const { diff --git a/spine-cocos2dx/3/src/spine/SkeletonRenderer.h b/spine-cocos2dx/3/src/spine/SkeletonRenderer.h index e22bbedf2..05f0b5534 100644 --- a/spine-cocos2dx/3/src/spine/SkeletonRenderer.h +++ b/spine-cocos2dx/3/src/spine/SkeletonRenderer.h @@ -37,7 +37,7 @@ namespace spine { -class PolygonBatch; +class AttachmentVertices; /* Draws a skeleton. */ class SkeletonRenderer: public cocos2d::Node, public cocos2d::BlendProtocol { @@ -115,12 +115,13 @@ CC_CONSTRUCTOR_ACCESS: protected: void setSkeletonData (spSkeletonData* skeletonData, bool ownsSkeletonData); - virtual cocos2d::Texture2D* getTexture (spRegionAttachment* attachment) const; - virtual cocos2d::Texture2D* getTexture (spMeshAttachment* attachment) const; - virtual cocos2d::Texture2D* getTexture (spWeightedMeshAttachment* attachment) const; + virtual AttachmentVertices* getAttachmentVertices (spRegionAttachment* attachment) const; + virtual AttachmentVertices* getAttachmentVertices (spMeshAttachment* attachment) const; + virtual AttachmentVertices* getAttachmentVertices (spWeightedMeshAttachment* attachment) const; bool _ownsSkeletonData; spAtlas* _atlas; + spAttachmentLoader* _attachmentLoader; cocos2d::CustomCommand _debugCommand; cocos2d::BlendFunc _blendFunc; float* _worldVertices; diff --git a/spine-cocos2dx/3/src/spine/spine-cocos2dx.h b/spine-cocos2dx/3/src/spine/spine-cocos2dx.h index f96eba024..31f56ed40 100644 --- a/spine-cocos2dx/3/src/spine/spine-cocos2dx.h +++ b/spine-cocos2dx/3/src/spine/spine-cocos2dx.h @@ -34,6 +34,7 @@ #include #include "cocos2d.h" +#include #include #include