diff --git a/spine-cocos2dx/README.md b/spine-cocos2dx/README.md index 2cefd2b16..b42d3df73 100644 --- a/spine-cocos2dx/README.md +++ b/spine-cocos2dx/README.md @@ -69,8 +69,8 @@ The Spine cocos2d-x example works on Windows and Mac OS X. ## Notes -- Images are premultiplied by cocos2d-x, so the Spine atlas images should *not* use premultiplied alpha. -- Two color tinting needs to be enabled on a per-skeleton basis. Call `SkeletonRenderer::setTwoColorTine(true)` or `SkeletonAnimation::setTwoColorTint(true)` after you created the skeleton instance. Note that two color tinting requires a custom shader and vertex format. Skeletons rendered with two color tinting can therefore not be batched with single color tinted skeletons or other 2D cocos2d-x elements like sprites. However, two-color tinted skeletons will be batched if possible when rendered after one another. +* Images are premultiplied by cocos2d-x, so the Spine atlas images should *not* use premultiplied alpha. +* Two color tinting needs to be enabled on a per-skeleton basis. Call `SkeletonRenderer::setTwoColorTine(true)` or `SkeletonAnimation::setTwoColorTint(true)` after you created the skeleton instance. Note that two color tinting requires a custom shader and vertex format. Skeletons rendered with two color tinting can therefore not be batched with single color tinted skeletons or other 2D cocos2d-x elements like sprites. However, two-color tinted skeletons will be batched if possible when rendered after one another. Attaching a child to a two color tinted skeleton will also break the batch. ## Examples diff --git a/spine-cocos2dx/example/Classes/BatchingExample.cpp b/spine-cocos2dx/example/Classes/BatchingExample.cpp index ee15868c1..594e3fca1 100644 --- a/spine-cocos2dx/example/Classes/BatchingExample.cpp +++ b/spine-cocos2dx/example/Classes/BatchingExample.cpp @@ -65,7 +65,7 @@ bool BatchingExample::init () { int xMin = _contentSize.width * 0.10f, xMax = _contentSize.width * 0.90f; int yMin = 0, yMax = _contentSize.height * 0.7f; - for (int i = 0; i < 100; i++) { + for (int i = 0, j = 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); @@ -74,7 +74,11 @@ bool BatchingExample::init () { skeletonNode->addAnimation(0, "jump", true, RandomHelper::random_int(0, 300) / 100.0f); skeletonNode->addAnimation(0, "run", true); - // skeletonNode->setTwoColorTint(true); + // alternative setting two color tint for groups of 10 skeletons + // should end up with #skeletons / 10 batches + if (j++ < 10) + skeletonNode->setTwoColorTint(true); + if (j == 20) j = 0; skeletonNode->setPosition(Vec2( RandomHelper::random_int(xMin, xMax), diff --git a/spine-cocos2dx/src/spine/SkeletonRenderer.cpp b/spine-cocos2dx/src/spine/SkeletonRenderer.cpp index ace2eeb76..3b5ec595e 100644 --- a/spine-cocos2dx/src/spine/SkeletonRenderer.cpp +++ b/spine-cocos2dx/src/spine/SkeletonRenderer.cpp @@ -67,7 +67,6 @@ 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) { @@ -332,7 +331,42 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t } } - if (lastTwoColorTrianglesCommand) lastTwoColorTrianglesCommand->setForceFlush(true); + if (lastTwoColorTrianglesCommand) { + Node* parent = this->getParent(); + + // We need to decide if we can postpone flushing the current + // batch. We can postpone if the next sibling node is a + // two color tinted skeleton with the same global-z. + // The parent->getChildrenCount() > 100 check is a hack + // as checking for a sibling is an O(n) operation, and if + // all children of this nodes parent are skeletons, we + // are in O(n2) territory. + if (!parent || parent->getChildrenCount() > 100 || getChildrenCount() != 0) { + lastTwoColorTrianglesCommand->setForceFlush(true); + } else { + Vector& children = parent->getChildren(); + Node* sibling = nullptr; + for (ssize_t i = 0; i < children.size(); i++) { + if (children.at(i) == this) { + if (i < children.size() - 1) { + sibling = children.at(i+1); + break; + } + } + } + if (!sibling) { + lastTwoColorTrianglesCommand->setForceFlush(true); + } else { + SkeletonRenderer* siblingSkeleton = dynamic_cast(sibling); + if (!siblingSkeleton || // flush is next sibling isn't a SkeletonRenderer + !siblingSkeleton->isTwoColorTint() || // flush if next sibling isn't two color tinted + !siblingSkeleton->isVisible() || // flush if next sibling is two color tinted but not visible + (siblingSkeleton->getGlobalZOrder() != this->getGlobalZOrder())) { // flush if next sibling is two color tinted but z-order differs + lastTwoColorTrianglesCommand->setForceFlush(true); + } + } + } + } if (_debugSlots || _debugBones) { drawDebug(renderer, transform, transformFlags); diff --git a/spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp b/spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp index 0501a93ef..d9c081c27 100644 --- a/spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp +++ b/spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp @@ -226,7 +226,7 @@ V3F_C4B_C4B_T2F* SkeletonTwoColorBatch::allocateVertices(uint32_t numVertices) { 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); + renderer->addCommand(command); return command; }