diff --git a/spine-cocos2dx/src/spine/SkeletonRenderer.cpp b/spine-cocos2dx/src/spine/SkeletonRenderer.cpp index ca33120ab..204c834fb 100644 --- a/spine-cocos2dx/src/spine/SkeletonRenderer.cpp +++ b/spine-cocos2dx/src/spine/SkeletonRenderer.cpp @@ -66,1014 +66,1014 @@ namespace spine { #endif -SkeletonRenderer* SkeletonRenderer::createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData) { - SkeletonRenderer* node = new SkeletonRenderer(skeleton, ownsSkeleton, ownsSkeletonData); - node->autorelease(); - return node; -} - -SkeletonRenderer* SkeletonRenderer::createWithData (SkeletonData* skeletonData, bool ownsSkeletonData) { - SkeletonRenderer* node = new SkeletonRenderer(skeletonData, ownsSkeletonData); - node->autorelease(); - return node; -} - -SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { - SkeletonRenderer* node = new SkeletonRenderer(skeletonDataFile, atlas, scale); - node->autorelease(); - return node; -} - -SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { - SkeletonRenderer* node = new SkeletonRenderer(skeletonDataFile, atlasFile, scale); - node->autorelease(); - return node; -} - -void SkeletonRenderer::initialize () { - _clipper = new (__FILE__, __LINE__) SkeletonClipping(); - - _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED; - setOpacityModifyRGB(true); - - setupGLProgramState(false); - - _skeleton->setToSetupPose(); - _skeleton->updateWorldTransform(); -} - -void SkeletonRenderer::setupGLProgramState (bool twoColorTintEnabled) { - if (twoColorTintEnabled) { - setGLProgramState(SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState()); - return; + SkeletonRenderer* SkeletonRenderer::createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData) { + SkeletonRenderer* node = new SkeletonRenderer(skeleton, ownsSkeleton, ownsSkeletonData); + node->autorelease(); + return node; } - Texture2D *texture = nullptr; - for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) { - Slot* slot = _skeleton->getDrawOrder()[i]; - Attachment* const attachment = slot->getAttachment(); - if (!attachment) continue; - if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { - RegionAttachment* regionAttachment = static_cast(attachment); - texture = static_cast(regionAttachment->getRendererObject())->_texture; - } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { - MeshAttachment* meshAttachment = static_cast(attachment); - texture = static_cast(meshAttachment->getRendererObject())->_texture; - } - else { - continue; + SkeletonRenderer* SkeletonRenderer::createWithData (SkeletonData* skeletonData, bool ownsSkeletonData) { + SkeletonRenderer* node = new SkeletonRenderer(skeletonData, ownsSkeletonData); + node->autorelease(); + return node; + } + + SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { + SkeletonRenderer* node = new SkeletonRenderer(skeletonDataFile, atlas, scale); + node->autorelease(); + return node; + } + + SkeletonRenderer* SkeletonRenderer::createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { + SkeletonRenderer* node = new SkeletonRenderer(skeletonDataFile, atlasFile, scale); + node->autorelease(); + return node; + } + + void SkeletonRenderer::initialize () { + _clipper = new (__FILE__, __LINE__) SkeletonClipping(); + + _blendFunc = BlendFunc::ALPHA_PREMULTIPLIED; + setOpacityModifyRGB(true); + + setupGLProgramState(false); + + _skeleton->setToSetupPose(); + _skeleton->updateWorldTransform(); + } + + void SkeletonRenderer::setupGLProgramState (bool twoColorTintEnabled) { + if (twoColorTintEnabled) { + setGLProgramState(SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState()); + return; } + + Texture2D *texture = nullptr; + for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) { + Slot* slot = _skeleton->getDrawOrder()[i]; + Attachment* const attachment = slot->getAttachment(); + if (!attachment) continue; + if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment* regionAttachment = static_cast(attachment); + texture = static_cast(regionAttachment->getRendererObject())->_texture; + } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { + MeshAttachment* meshAttachment = static_cast(attachment); + texture = static_cast(meshAttachment->getRendererObject())->_texture; + } + else { + continue; + } - if (texture != nullptr) { - break; + if (texture != nullptr) { + break; + } } + setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, texture)); } - setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, texture)); -} -void SkeletonRenderer::setSkeletonData (SkeletonData *skeletonData, bool ownsSkeletonData) { - _skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData); - _ownsSkeletonData = ownsSkeletonData; -} + void SkeletonRenderer::setSkeletonData (SkeletonData *skeletonData, bool ownsSkeletonData) { + _skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData); + _ownsSkeletonData = ownsSkeletonData; + } -SkeletonRenderer::SkeletonRenderer () - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { -} + SkeletonRenderer::SkeletonRenderer () + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { + } -SkeletonRenderer::SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { - initWithSkeleton(skeleton, ownsSkeleton, ownsSkeletonData, ownsAtlas); -} + SkeletonRenderer::SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { + initWithSkeleton(skeleton, ownsSkeleton, ownsSkeletonData, ownsAtlas); + } -SkeletonRenderer::SkeletonRenderer (SkeletonData *skeletonData, bool ownsSkeletonData) - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { - initWithData(skeletonData, ownsSkeletonData); -} + SkeletonRenderer::SkeletonRenderer (SkeletonData *skeletonData, bool ownsSkeletonData) + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { + initWithData(skeletonData, ownsSkeletonData); + } -SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, Atlas* atlas, float scale) - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { - initWithJsonFile(skeletonDataFile, atlas, scale); -} + SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, Atlas* atlas, float scale) + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { + initWithJsonFile(skeletonDataFile, atlas, scale); + } -SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) - : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { - initWithJsonFile(skeletonDataFile, atlasFile, scale); -} + SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) + : _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits::max()) { + initWithJsonFile(skeletonDataFile, atlasFile, scale); + } -SkeletonRenderer::~SkeletonRenderer () { - if (_ownsSkeletonData) delete _skeleton->getData(); - if (_ownsSkeleton) delete _skeleton; - if (_ownsAtlas) delete _atlas; - delete _attachmentLoader; - delete _clipper; -} + SkeletonRenderer::~SkeletonRenderer () { + if (_ownsSkeletonData) delete _skeleton->getData(); + if (_ownsSkeleton) delete _skeleton; + if (_ownsAtlas) delete _atlas; + delete _attachmentLoader; + delete _clipper; + } -void SkeletonRenderer::initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) { - _skeleton = skeleton; - _ownsSkeleton = ownsSkeleton; - _ownsSkeletonData = ownsSkeletonData; - _ownsAtlas = ownsAtlas; - initialize(); -} + void SkeletonRenderer::initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) { + _skeleton = skeleton; + _ownsSkeleton = ownsSkeleton; + _ownsSkeletonData = ownsSkeletonData; + _ownsAtlas = ownsAtlas; + initialize(); + } -void SkeletonRenderer::initWithData (SkeletonData* skeletonData, bool ownsSkeletonData) { - _ownsSkeleton = true; - setSkeletonData(skeletonData, ownsSkeletonData); - initialize(); -} + void SkeletonRenderer::initWithData (SkeletonData* skeletonData, bool ownsSkeletonData) { + _ownsSkeleton = true; + setSkeletonData(skeletonData, ownsSkeletonData); + initialize(); + } -void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { - _atlas = atlas; - _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); + void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { + _atlas = atlas; + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); - SkeletonJson json(_attachmentLoader); - json.setScale(scale); - SkeletonData* skeletonData = json.readSkeletonDataFile(skeletonDataFile.c_str()); - CCASSERT(skeletonData, !json.getError().isEmpty() ? json.getError().buffer() : "Error reading skeleton data."); + SkeletonJson json(_attachmentLoader); + json.setScale(scale); + SkeletonData* skeletonData = json.readSkeletonDataFile(skeletonDataFile.c_str()); + CCASSERT(skeletonData, !json.getError().isEmpty() ? json.getError().buffer() : "Error reading skeleton data."); - _ownsSkeleton = true; - setSkeletonData(skeletonData, true); + _ownsSkeleton = true; + setSkeletonData(skeletonData, true); - initialize(); -} + initialize(); + } -void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { - _atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader, true); - CCASSERT(_atlas, "Error reading atlas file."); + void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { + _atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader, true); + CCASSERT(_atlas, "Error reading atlas file."); - _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); - SkeletonJson json(_attachmentLoader); - json.setScale(scale); - SkeletonData* skeletonData = json.readSkeletonDataFile(skeletonDataFile.c_str()); - CCASSERT(skeletonData, !json.getError().isEmpty() ? json.getError().buffer() : "Error reading skeleton data."); + SkeletonJson json(_attachmentLoader); + json.setScale(scale); + SkeletonData* skeletonData = json.readSkeletonDataFile(skeletonDataFile.c_str()); + CCASSERT(skeletonData, !json.getError().isEmpty() ? json.getError().buffer() : "Error reading skeleton data."); - _ownsSkeleton = true; - _ownsAtlas = true; - setSkeletonData(skeletonData, true); + _ownsSkeleton = true; + _ownsAtlas = true; + setSkeletonData(skeletonData, true); - initialize(); -} + initialize(); + } -void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { - _atlas = atlas; - _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); + void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) { + _atlas = atlas; + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); - SkeletonBinary binary(_attachmentLoader); - binary.setScale(scale); - SkeletonData* skeletonData = binary.readSkeletonDataFile(skeletonDataFile.c_str()); - CCASSERT(skeletonData, !binary.getError().isEmpty() ? binary.getError().buffer() : "Error reading skeleton data."); - _ownsSkeleton = true; - setSkeletonData(skeletonData, true); + SkeletonBinary binary(_attachmentLoader); + binary.setScale(scale); + SkeletonData* skeletonData = binary.readSkeletonDataFile(skeletonDataFile.c_str()); + CCASSERT(skeletonData, !binary.getError().isEmpty() ? binary.getError().buffer() : "Error reading skeleton data."); + _ownsSkeleton = true; + setSkeletonData(skeletonData, true); - initialize(); -} - -void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { - _atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader, true); - CCASSERT(_atlas, "Error reading atlas file."); - - _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); - - SkeletonBinary binary(_attachmentLoader); - binary.setScale(scale); - SkeletonData* skeletonData = binary.readSkeletonDataFile(skeletonDataFile.c_str()); - CCASSERT(skeletonData, !binary.getError().isEmpty() ? binary.getError().buffer() : "Error reading skeleton data."); - _ownsSkeleton = true; - _ownsAtlas = true; - setSkeletonData(skeletonData, true); - - initialize(); -} - - -void SkeletonRenderer::update (float deltaTime) { - Node::update(deltaTime); - if (_ownsSkeleton) _skeleton->update(deltaTime * _timeScale); -} - -void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t transformFlags) { - _isAutoCulled = false; - // Early exit if the skeleton is invisible - if (getDisplayedOpacity() == 0 || _skeleton->getColor().a == 0){ - return; + initialize(); } - const int coordCount = computeTotalCoordCount(*_skeleton, _startSlotIndex, _endSlotIndex); - if (coordCount == 0) - { - return; - } - assert(coordCount % 2 == 0); - - VLA(float, worldCoords, coordCount); - transformWorldVertices(worldCoords, coordCount, *_skeleton, _startSlotIndex, _endSlotIndex); - - #if CC_USE_CULLING - const Camera* camera = Camera::getVisitingCamera(); - const cocos2d::Rect brect = computeBoundingRect(worldCoords, coordCount / 2); - _boundingRect = brect; - - const bool autoCullingEnable = cocos2d::Director::getInstance()->isAutoCullingEnable(); - if (autoCullingEnable && camera && cullRectangle(transform, brect, *camera)) - { - VLA_FREE(worldCoords); - _isAutoCulled = true; - return; - } - #endif - - const float* worldCoordPtr = worldCoords; - SkeletonBatch* batch = SkeletonBatch::getInstance(); - SkeletonTwoColorBatch* twoColorBatch = SkeletonTwoColorBatch::getInstance(); - const bool hasSingleTint = (isTwoColorTint() == false); - - if (_effect) { - _effect->begin(*_skeleton); - } - - const Color3B displayedColor = getDisplayedColor(); - Color nodeColor; - nodeColor.r = displayedColor.r / 255.f; - nodeColor.g = displayedColor.g / 255.f; - nodeColor.b = displayedColor.b / 255.f; - nodeColor.a = getDisplayedOpacity() / 255.f; + void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) { + _atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader, true); + CCASSERT(_atlas, "Error reading atlas file."); - Color color; - Color darkColor; - const float darkPremultipliedAlpha = _premultipliedAlpha ? 1.f : 0; - AttachmentVertices* attachmentVertices = nullptr; - TwoColorTrianglesCommand* lastTwoColorTrianglesCommand = nullptr; - for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) { - Slot* slot = _skeleton->getDrawOrder()[i];; + _attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas); + + SkeletonBinary binary(_attachmentLoader); + binary.setScale(scale); + SkeletonData* skeletonData = binary.readSkeletonDataFile(skeletonDataFile.c_str()); + CCASSERT(skeletonData, !binary.getError().isEmpty() ? binary.getError().buffer() : "Error reading skeleton data."); + _ownsSkeleton = true; + _ownsAtlas = true; + setSkeletonData(skeletonData, true); + + initialize(); + } + + + void SkeletonRenderer::update (float deltaTime) { + Node::update(deltaTime); + if (_ownsSkeleton) _skeleton->update(deltaTime * _timeScale); + } + + void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t transformFlags) { + _isAutoCulled = false; + // Early exit if the skeleton is invisible + if (getDisplayedOpacity() == 0 || _skeleton->getColor().a == 0){ + return; + } + + const int coordCount = computeTotalCoordCount(*_skeleton, _startSlotIndex, _endSlotIndex); + if (coordCount == 0) + { + return; + } + assert(coordCount % 2 == 0); + + VLA(float, worldCoords, coordCount); + transformWorldVertices(worldCoords, coordCount, *_skeleton, _startSlotIndex, _endSlotIndex); + + #if CC_USE_CULLING + const Camera* camera = Camera::getVisitingCamera(); + const cocos2d::Rect brect = computeBoundingRect(worldCoords, coordCount / 2); + _boundingRect = brect; + + const bool autoCullingEnable = cocos2d::Director::getInstance()->isAutoCullingEnable(); + if (autoCullingEnable && camera && cullRectangle(transform, brect, *camera)) + { + VLA_FREE(worldCoords); + _isAutoCulled = true; + return; + } + #endif + + const float* worldCoordPtr = worldCoords; + SkeletonBatch* batch = SkeletonBatch::getInstance(); + SkeletonTwoColorBatch* twoColorBatch = SkeletonTwoColorBatch::getInstance(); + const bool hasSingleTint = (isTwoColorTint() == false); + + if (_effect) { + _effect->begin(*_skeleton); + } + + const Color3B displayedColor = getDisplayedColor(); + Color nodeColor; + nodeColor.r = displayedColor.r / 255.f; + nodeColor.g = displayedColor.g / 255.f; + nodeColor.b = displayedColor.b / 255.f; + nodeColor.a = getDisplayedOpacity() / 255.f; + + Color color; + Color darkColor; + const float darkPremultipliedAlpha = _premultipliedAlpha ? 1.f : 0; + AttachmentVertices* attachmentVertices = nullptr; + TwoColorTrianglesCommand* lastTwoColorTrianglesCommand = nullptr; + for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) { + Slot* slot = _skeleton->getDrawOrder()[i];; - if (slotIsOutRange(*slot, _startSlotIndex, _endSlotIndex)) { - _clipper->clipEnd(*slot); - continue; - } - - if (!slot->getAttachment()) { - _clipper->clipEnd(*slot); - continue; - } - - // Early exit if slot is invisible - if (slot->getColor().a == 0) { - _clipper->clipEnd(*slot); - continue; - } - - cocos2d::TrianglesCommand::Triangles triangles; - TwoColorTriangles trianglesTwoColor; - - if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) { - RegionAttachment* attachment = static_cast(slot->getAttachment()); - attachmentVertices = static_cast(attachment->getRendererObject()); - - // Early exit if attachment is invisible - if (attachment->getColor().a == 0) { + if (slotIsOutRange(*slot, _startSlotIndex, _endSlotIndex)) { _clipper->clipEnd(*slot); continue; } - float* dstTriangleVertices = nullptr; - int dstStride = 0; // in floats - if (hasSingleTint) { - triangles.indices = attachmentVertices->_triangles->indices; - triangles.indexCount = attachmentVertices->_triangles->indexCount; - triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount); - triangles.vertCount = attachmentVertices->_triangles->vertCount; - assert(triangles.vertCount == 4); - memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount); - dstStride = sizeof(V3F_C4B_T2F) / sizeof(float); - dstTriangleVertices = reinterpret_cast(triangles.verts); - } else { - trianglesTwoColor.indices = attachmentVertices->_triangles->indices; - trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount; - trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount); - trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount; - assert(trianglesTwoColor.vertCount == 4); - for (int v = 0; v < trianglesTwoColor.vertCount; v++) { - trianglesTwoColor.verts[v].texCoords = attachmentVertices->_triangles->verts[v].texCoords; - } - dstTriangleVertices = reinterpret_cast(trianglesTwoColor.verts); - dstStride = sizeof(V3F_C4B_C4B_T2F) / sizeof(float); + if (!slot->getAttachment()) { + _clipper->clipEnd(*slot); + continue; } - // Copy world vertices to triangle vertices - interleaveCoordinates(dstTriangleVertices, worldCoordPtr, 4, dstStride); - worldCoordPtr += 8; - - color = attachment->getColor(); - } - else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) { - MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment(); - attachmentVertices = (AttachmentVertices*)attachment->getRendererObject(); - - float* dstTriangleVertices = nullptr; - int dstStride = 0; // in floats - int dstVertexCount = 0; - if (hasSingleTint) { - 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); - dstTriangleVertices = (float*)triangles.verts; - dstStride = sizeof(V3F_C4B_T2F) / sizeof(float); - dstVertexCount = triangles.vertCount; - } 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 v = 0; v < trianglesTwoColor.vertCount; v++) { - trianglesTwoColor.verts[v].texCoords = attachmentVertices->_triangles->verts[v].texCoords; - } - dstTriangleVertices = (float*)trianglesTwoColor.verts; - dstStride = sizeof(V3F_C4B_C4B_T2F) / sizeof(float); - dstVertexCount = trianglesTwoColor.vertCount; - } - - // Copy world vertices to triangle vertices - //assert(dstVertexCount * 2 == attachment->super.worldVerticesLength); - interleaveCoordinates(dstTriangleVertices, worldCoordPtr, dstVertexCount, dstStride); - worldCoordPtr += dstVertexCount * 2; - - color = attachment->getColor(); - } - else if (slot->getAttachment()->getRTTI().isExactly(ClippingAttachment::rtti)) { - ClippingAttachment* clip = (ClippingAttachment*)slot->getAttachment(); - _clipper->clipStart(*slot, clip); - continue; - } else { - _clipper->clipEnd(*slot); - continue; - } - - if (slot->hasDarkColor()) { - darkColor = slot->getDarkColor(); - } else { - darkColor.r = 0; - darkColor.g = 0; - darkColor.b = 0; - } - darkColor.a = darkPremultipliedAlpha; - - color.a *= nodeColor.a * _skeleton->getColor().a * slot->getColor().a; - // skip rendering if the color of this attachment is 0 - if (color.a == 0){ - _clipper->clipEnd(*slot); - continue; - } - color.r *= nodeColor.r * _skeleton->getColor().r * slot->getColor().r; - color.g *= nodeColor.g * _skeleton->getColor().g * slot->getColor().g; - color.b *= nodeColor.b * _skeleton->getColor().b * slot->getColor().b; - if (_premultipliedAlpha) - { - color.r *= color.a; - color.g *= color.a; - color.b *= color.a; - } - - const cocos2d::Color4B color4B = ColorToColor4B(color); - const cocos2d::Color4B darkColor4B = ColorToColor4B(darkColor); - const BlendFunc blendFunc = makeBlendFunc(slot->getData().getBlendMode(), _premultipliedAlpha); - if (hasSingleTint) { - if (_clipper->isClipping()) { - _clipper->clipTriangles((float*)&triangles.verts[0].vertices, triangles.indices, triangles.indexCount, (float*)&triangles.verts[0].texCoords, sizeof(cocos2d::V3F_C4B_T2F) / 4); - batch->deallocateVertices(triangles.vertCount); - - if (_clipper->getClippedTriangles().size() == 0){ - _clipper->clipEnd(*slot); - continue; - } - - triangles.vertCount = _clipper->getClippedVertices().size() / 2; - triangles.verts = batch->allocateVertices(triangles.vertCount); - triangles.indexCount = _clipper->getClippedTriangles().size(); - triangles.indices = - batch->allocateIndices(triangles.indexCount); - memcpy(triangles.indices, _clipper->getClippedTriangles().buffer(), sizeof(unsigned short) * _clipper->getClippedTriangles().size()); - - cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags); - - const float* verts = _clipper->getClippedVertices().buffer(); - const float* uvs = _clipper->getClippedUVs().buffer(); - if (_effect) { - V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; - Color darkTmp; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2, ++vertex) { - Color lightCopy = color; - vertex->vertices.x = verts[vv]; - vertex->vertices.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - _effect->transform(vertex->vertices.x, vertex->vertices.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkTmp); - vertex->colors = ColorToColor4B(lightCopy); - } - } else { - V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2, ++vertex) { - vertex->vertices.x = verts[vv]; - vertex->vertices.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - vertex->colors = color4B; - } - } - } else { - // Not clipping - - cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags); - - if (_effect) { - V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; - Color darkTmp; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v, ++vertex) { - Color lightCopy = color; - _effect->transform(vertex->vertices.x, vertex->vertices.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkTmp); - vertex->colors = ColorToColor4B(lightCopy); - } - } else { - V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v, ++vertex) { - vertex->colors = color4B; - } - } + // Early exit if slot is invisible + if (slot->getColor().a == 0) { + _clipper->clipEnd(*slot); + continue; } - } else { - // Two tints - - if (_clipper->isClipping()) { - _clipper->clipTriangles((float*)&trianglesTwoColor.verts[0].position, trianglesTwoColor.indices, trianglesTwoColor.indexCount, (float*)&trianglesTwoColor.verts[0].texCoords, sizeof(V3F_C4B_C4B_T2F) / 4); - twoColorBatch->deallocateVertices(trianglesTwoColor.vertCount); - - if (_clipper->getClippedTriangles().size() == 0){ - _clipper->clipEnd(*slot); - continue; - } - - trianglesTwoColor.vertCount = _clipper->getClippedVertices().size() / 2; - trianglesTwoColor.verts = twoColorBatch->allocateVertices(trianglesTwoColor.vertCount); - trianglesTwoColor.indexCount = _clipper->getClippedTriangles().size(); - trianglesTwoColor.indices = twoColorBatch->allocateIndices(trianglesTwoColor.indexCount); - memcpy(trianglesTwoColor.indices, _clipper->getClippedTriangles().buffer(), sizeof(unsigned short) * _clipper->getClippedTriangles().size()); - - TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags); - - const float* verts = _clipper->getClippedVertices().buffer(); - const float* uvs = _clipper->getClippedUVs().buffer(); - - if (_effect) { - V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2, ++vertex) { - Color lightCopy = color; - Color darkCopy = darkColor; - vertex->position.x = verts[vv]; - vertex->position.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - _effect->transform(vertex->position.x, vertex->position.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy); - vertex->color = ColorToColor4B(lightCopy); - vertex->color2 = ColorToColor4B(darkCopy); - } - } else { - V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2, ++vertex) { - vertex->position.x = verts[vv]; - vertex->position.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - vertex->color = color4B; - vertex->color2 = darkColor4B; - } - } - } else { - TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags); - - if (_effect) { - V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v, ++vertex) { - Color lightCopy = color; - Color darkCopy = darkColor; - _effect->transform(vertex->position.x, vertex->position.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy); - vertex->color = ColorToColor4B(lightCopy); - vertex->color2 = ColorToColor4B(darkCopy); - } - } else { - V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; - for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v, ++vertex) { - vertex->color = color4B; - vertex->color2 = darkColor4B; - } - } - } - } - _clipper->clipEnd(*slot); - } - _clipper->clipEnd(); - - if (lastTwoColorTrianglesCommand) { - Node* parent = this->getParent(); + + cocos2d::TrianglesCommand::Triangles triangles; + TwoColorTriangles trianglesTwoColor; - // 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 { - const cocos2d::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 (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment* attachment = static_cast(slot->getAttachment()); + attachmentVertices = static_cast(attachment->getRendererObject()); + + // Early exit if attachment is invisible + if (attachment->getColor().a == 0) { + _clipper->clipEnd(*slot); + continue; + } + + float* dstTriangleVertices = nullptr; + int dstStride = 0; // in floats + if (hasSingleTint) { + triangles.indices = attachmentVertices->_triangles->indices; + triangles.indexCount = attachmentVertices->_triangles->indexCount; + triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount); + triangles.vertCount = attachmentVertices->_triangles->vertCount; + assert(triangles.vertCount == 4); + memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount); + dstStride = sizeof(V3F_C4B_T2F) / sizeof(float); + dstTriangleVertices = reinterpret_cast(triangles.verts); + } else { + trianglesTwoColor.indices = attachmentVertices->_triangles->indices; + trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount; + trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount); + trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount; + assert(trianglesTwoColor.vertCount == 4); + for (int v = 0; v < trianglesTwoColor.vertCount; v++) { + trianglesTwoColor.verts[v].texCoords = attachmentVertices->_triangles->verts[v].texCoords; + } + dstTriangleVertices = reinterpret_cast(trianglesTwoColor.verts); + dstStride = sizeof(V3F_C4B_C4B_T2F) / sizeof(float); + } + // Copy world vertices to triangle vertices + interleaveCoordinates(dstTriangleVertices, worldCoordPtr, 4, dstStride); + worldCoordPtr += 8; + + color = attachment->getColor(); + } + else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) { + MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment(); + attachmentVertices = (AttachmentVertices*)attachment->getRendererObject(); + + float* dstTriangleVertices = nullptr; + int dstStride = 0; // in floats + int dstVertexCount = 0; + if (hasSingleTint) { + 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); + dstTriangleVertices = (float*)triangles.verts; + dstStride = sizeof(V3F_C4B_T2F) / sizeof(float); + dstVertexCount = triangles.vertCount; + } 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 v = 0; v < trianglesTwoColor.vertCount; v++) { + trianglesTwoColor.verts[v].texCoords = attachmentVertices->_triangles->verts[v].texCoords; + } + dstTriangleVertices = (float*)trianglesTwoColor.verts; + dstStride = sizeof(V3F_C4B_C4B_T2F) / sizeof(float); + dstVertexCount = trianglesTwoColor.vertCount; + } + + // Copy world vertices to triangle vertices + //assert(dstVertexCount * 2 == attachment->super.worldVerticesLength); + interleaveCoordinates(dstTriangleVertices, worldCoordPtr, dstVertexCount, dstStride); + worldCoordPtr += dstVertexCount * 2; + + color = attachment->getColor(); + } + else if (slot->getAttachment()->getRTTI().isExactly(ClippingAttachment::rtti)) { + ClippingAttachment* clip = (ClippingAttachment*)slot->getAttachment(); + _clipper->clipStart(*slot, clip); + continue; + } else { + _clipper->clipEnd(*slot); + continue; + } + + if (slot->hasDarkColor()) { + darkColor = slot->getDarkColor(); + } else { + darkColor.r = 0; + darkColor.g = 0; + darkColor.b = 0; + } + darkColor.a = darkPremultipliedAlpha; + + color.a *= nodeColor.a * _skeleton->getColor().a * slot->getColor().a; + // skip rendering if the color of this attachment is 0 + if (color.a == 0){ + _clipper->clipEnd(*slot); + continue; + } + color.r *= nodeColor.r * _skeleton->getColor().r * slot->getColor().r; + color.g *= nodeColor.g * _skeleton->getColor().g * slot->getColor().g; + color.b *= nodeColor.b * _skeleton->getColor().b * slot->getColor().b; + if (_premultipliedAlpha) + { + color.r *= color.a; + color.g *= color.a; + color.b *= color.a; + } + + const cocos2d::Color4B color4B = ColorToColor4B(color); + const cocos2d::Color4B darkColor4B = ColorToColor4B(darkColor); + const BlendFunc blendFunc = makeBlendFunc(slot->getData().getBlendMode(), _premultipliedAlpha); + + if (hasSingleTint) { + if (_clipper->isClipping()) { + _clipper->clipTriangles((float*)&triangles.verts[0].vertices, triangles.indices, triangles.indexCount, (float*)&triangles.verts[0].texCoords, sizeof(cocos2d::V3F_C4B_T2F) / 4); + batch->deallocateVertices(triangles.vertCount); + + if (_clipper->getClippedTriangles().size() == 0){ + _clipper->clipEnd(*slot); + continue; + } + + triangles.vertCount = _clipper->getClippedVertices().size() / 2; + triangles.verts = batch->allocateVertices(triangles.vertCount); + triangles.indexCount = _clipper->getClippedTriangles().size(); + triangles.indices = + batch->allocateIndices(triangles.indexCount); + memcpy(triangles.indices, _clipper->getClippedTriangles().buffer(), sizeof(unsigned short) * _clipper->getClippedTriangles().size()); + + cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags); + + const float* verts = _clipper->getClippedVertices().buffer(); + const float* uvs = _clipper->getClippedUVs().buffer(); + if (_effect) { + V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; + Color darkTmp; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2, ++vertex) { + Color lightCopy = color; + vertex->vertices.x = verts[vv]; + vertex->vertices.y = verts[vv + 1]; + vertex->texCoords.u = uvs[vv]; + vertex->texCoords.v = uvs[vv + 1]; + _effect->transform(vertex->vertices.x, vertex->vertices.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkTmp); + vertex->colors = ColorToColor4B(lightCopy); + } + } else { + V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2, ++vertex) { + vertex->vertices.x = verts[vv]; + vertex->vertices.y = verts[vv + 1]; + vertex->texCoords.u = uvs[vv]; + vertex->texCoords.v = uvs[vv + 1]; + vertex->colors = color4B; + } + } + } else { + // Not clipping + + cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags); + + if (_effect) { + V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; + Color darkTmp; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v, ++vertex) { + Color lightCopy = color; + _effect->transform(vertex->vertices.x, vertex->vertices.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkTmp); + vertex->colors = ColorToColor4B(lightCopy); + } + } else { + V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v, ++vertex) { + vertex->colors = color4B; + } + } + } + } else { + // Two tints + + if (_clipper->isClipping()) { + _clipper->clipTriangles((float*)&trianglesTwoColor.verts[0].position, trianglesTwoColor.indices, trianglesTwoColor.indexCount, (float*)&trianglesTwoColor.verts[0].texCoords, sizeof(V3F_C4B_C4B_T2F) / 4); + twoColorBatch->deallocateVertices(trianglesTwoColor.vertCount); + + if (_clipper->getClippedTriangles().size() == 0){ + _clipper->clipEnd(*slot); + continue; + } + + trianglesTwoColor.vertCount = _clipper->getClippedVertices().size() / 2; + trianglesTwoColor.verts = twoColorBatch->allocateVertices(trianglesTwoColor.vertCount); + trianglesTwoColor.indexCount = _clipper->getClippedTriangles().size(); + trianglesTwoColor.indices = twoColorBatch->allocateIndices(trianglesTwoColor.indexCount); + memcpy(trianglesTwoColor.indices, _clipper->getClippedTriangles().buffer(), sizeof(unsigned short) * _clipper->getClippedTriangles().size()); + + TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags); + + const float* verts = _clipper->getClippedVertices().buffer(); + const float* uvs = _clipper->getClippedUVs().buffer(); + + if (_effect) { + V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2, ++vertex) { + Color lightCopy = color; + Color darkCopy = darkColor; + vertex->position.x = verts[vv]; + vertex->position.y = verts[vv + 1]; + vertex->texCoords.u = uvs[vv]; + vertex->texCoords.v = uvs[vv + 1]; + _effect->transform(vertex->position.x, vertex->position.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy); + vertex->color = ColorToColor4B(lightCopy); + vertex->color2 = ColorToColor4B(darkCopy); + } + } else { + V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2, ++vertex) { + vertex->position.x = verts[vv]; + vertex->position.y = verts[vv + 1]; + vertex->texCoords.u = uvs[vv]; + vertex->texCoords.v = uvs[vv + 1]; + vertex->color = color4B; + vertex->color2 = darkColor4B; + } + } + } else { + TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags); + + if (_effect) { + V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v, ++vertex) { + Color lightCopy = color; + Color darkCopy = darkColor; + _effect->transform(vertex->position.x, vertex->position.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy); + vertex->color = ColorToColor4B(lightCopy); + vertex->color2 = ColorToColor4B(darkCopy); + } + } else { + V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts; + for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v, ++vertex) { + vertex->color = color4B; + vertex->color2 = darkColor4B; + } } } } - if (!sibling) { + _clipper->clipEnd(*slot); + } + _clipper->clipEnd(); + + 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 { - 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 + const cocos2d::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 (_effect) _effect->end(); + if (_effect) _effect->end(); - if (_debugBoundingRect || _debugSlots || _debugBones || _debugMeshes) { - drawDebug(renderer, transform, transformFlags); + if (_debugBoundingRect || _debugSlots || _debugBones || _debugMeshes) { + drawDebug(renderer, transform, transformFlags); + } + + VLA_FREE(worldCoords); } - VLA_FREE(worldCoords); -} + bool SkeletonRenderer::isAutoCulled () const { + return _isAutoCulled; + } -bool SkeletonRenderer::isAutoCulled () const { - return _isAutoCulled; -} + void SkeletonRenderer::drawDebug (Renderer* renderer, const Mat4 &transform, uint32_t transformFlags) { -void SkeletonRenderer::drawDebug (Renderer* renderer, const Mat4 &transform, uint32_t transformFlags) { + #if !defined(USE_MATRIX_STACK_PROJECTION_ONLY) + Director* director = Director::getInstance(); + director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); + director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform); + #endif - #if !defined(USE_MATRIX_STACK_PROJECTION_ONLY) - Director* director = Director::getInstance(); - director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); - director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform); -#endif - - DrawNode* drawNode = DrawNode::create(); + DrawNode* drawNode = DrawNode::create(); - // Draw bounding rectangle - if (_debugBoundingRect) { - glLineWidth(2); - const cocos2d::Rect brect = getBoundingBox(); - const Vec2 points[4] = - { - brect.origin, - { brect.origin.x + brect.size.width, brect.origin.y }, - { brect.origin.x + brect.size.width, brect.origin.y + brect.size.height }, - { brect.origin.x, brect.origin.y + brect.size.height } - }; - drawNode->drawPoly(points, 4, true, Color4F::GREEN); - } - - if (_debugSlots) { - // Slots. - // DrawPrimitives::setDrawColor4B(0, 0, 255, 255); - glLineWidth(1); - V3F_C4B_T2F_Quad quad; - for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) { - Slot* slot = _skeleton->getDrawOrder()[i]; - if (!slot->getAttachment() || !slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) { - continue; - } - if (slotIsOutRange(*slot, _startSlotIndex, _endSlotIndex)) { - continue; - } - RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment(); - float worldVertices[8]; - attachment->computeWorldVertices(slot->getBone(), worldVertices, 0, 2); + // Draw bounding rectangle + if (_debugBoundingRect) { + glLineWidth(2); + const cocos2d::Rect brect = getBoundingBox(); const Vec2 points[4] = { - { worldVertices[0], worldVertices[1] }, - { worldVertices[2], worldVertices[3] }, - { worldVertices[4], worldVertices[5] }, - { worldVertices[6], worldVertices[7] } + brect.origin, + { brect.origin.x + brect.size.width, brect.origin.y }, + { brect.origin.x + brect.size.width, brect.origin.y + brect.size.height }, + { brect.origin.x, brect.origin.y + brect.size.height } }; - drawNode->drawPoly(points, 4, true, Color4F::BLUE); + drawNode->drawPoly(points, 4, true, Color4F::GREEN); } - } - - if (_debugBones) { - // Bone lengths. - glLineWidth(2); - for (int i = 0, n = _skeleton->getBones().size(); i < n; i++) { - Bone *bone = _skeleton->getBones()[i]; - float x = bone->getData().getLength() * bone->getA() + bone->getWorldX(); - float y = bone->getData().getLength() * bone->getC() + bone->getWorldY(); - drawNode->drawLine(Vec2(bone->getWorldX(), bone->getWorldY()), Vec2(x, y), Color4F::RED); - } - // Bone origins. - auto color = Color4F::BLUE; // Root bone is blue. - for (int i = 0, n = _skeleton->getBones().size(); i < n; i++) { - Bone *bone = _skeleton->getBones()[i]; - drawNode->drawPoint(Vec2(bone->getWorldX(), bone->getWorldY()), 4, color); - if (i == 0) color = Color4F::GREEN; - } - } - - if (_debugMeshes) { - // Meshes. - glLineWidth(1); - for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) { - Slot* slot = _skeleton->getDrawOrder()[i]; - if (!slot->getAttachment() || !slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) continue; - MeshAttachment* const mesh = static_cast(slot->getAttachment()); - VLA(float, worldCoord, mesh->getWorldVerticesLength()); - mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), worldCoord, 0, 2); - for (size_t t = 0; t < mesh->getTriangles().size(); t += 3) { - // Fetch triangle indices - const int idx0 = mesh->getTriangles()[t + 0]; - const int idx1 = mesh->getTriangles()[t + 1]; - const int idx2 = mesh->getTriangles()[t + 2]; - const Vec2 v[3] = + + if (_debugSlots) { + // Slots. + // DrawPrimitives::setDrawColor4B(0, 0, 255, 255); + glLineWidth(1); + V3F_C4B_T2F_Quad quad; + for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) { + Slot* slot = _skeleton->getDrawOrder()[i]; + if (!slot->getAttachment() || !slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) { + continue; + } + if (slotIsOutRange(*slot, _startSlotIndex, _endSlotIndex)) { + continue; + } + RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment(); + float worldVertices[8]; + attachment->computeWorldVertices(slot->getBone(), worldVertices, 0, 2); + const Vec2 points[4] = { - worldCoord + (idx0 * 2), - worldCoord + (idx1 * 2), - worldCoord + (idx2 * 2) + { worldVertices[0], worldVertices[1] }, + { worldVertices[2], worldVertices[3] }, + { worldVertices[4], worldVertices[5] }, + { worldVertices[6], worldVertices[7] } }; - drawNode->drawPoly(v, 3, true, Color4F::YELLOW); + drawNode->drawPoly(points, 4, true, Color4F::BLUE); } - VLA_FREE(worldCoord); } + + if (_debugBones) { + // Bone lengths. + glLineWidth(2); + for (int i = 0, n = _skeleton->getBones().size(); i < n; i++) { + Bone *bone = _skeleton->getBones()[i]; + float x = bone->getData().getLength() * bone->getA() + bone->getWorldX(); + float y = bone->getData().getLength() * bone->getC() + bone->getWorldY(); + drawNode->drawLine(Vec2(bone->getWorldX(), bone->getWorldY()), Vec2(x, y), Color4F::RED); + } + // Bone origins. + auto color = Color4F::BLUE; // Root bone is blue. + for (int i = 0, n = _skeleton->getBones().size(); i < n; i++) { + Bone *bone = _skeleton->getBones()[i]; + drawNode->drawPoint(Vec2(bone->getWorldX(), bone->getWorldY()), 4, color); + if (i == 0) color = Color4F::GREEN; + } + } + + if (_debugMeshes) { + // Meshes. + glLineWidth(1); + for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) { + Slot* slot = _skeleton->getDrawOrder()[i]; + if (!slot->getAttachment() || !slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) continue; + MeshAttachment* const mesh = static_cast(slot->getAttachment()); + VLA(float, worldCoord, mesh->getWorldVerticesLength()); + mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), worldCoord, 0, 2); + for (size_t t = 0; t < mesh->getTriangles().size(); t += 3) { + // Fetch triangle indices + const int idx0 = mesh->getTriangles()[t + 0]; + const int idx1 = mesh->getTriangles()[t + 1]; + const int idx2 = mesh->getTriangles()[t + 2]; + const Vec2 v[3] = + { + worldCoord + (idx0 * 2), + worldCoord + (idx1 * 2), + worldCoord + (idx2 * 2) + }; + drawNode->drawPoly(v, 3, true, Color4F::YELLOW); + } + VLA_FREE(worldCoord); + } + } + + drawNode->draw(renderer, transform, transformFlags); + #if !defined(USE_MATRIX_STACK_PROJECTION_ONLY) + director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); + #endif + } + + cocos2d::Rect SkeletonRenderer::getBoundingBox () const { + return _boundingRect; + } + + // --- Convenience methods for Skeleton_* functions. + + void SkeletonRenderer::updateWorldTransform() { + _skeleton->updateWorldTransform(); + } + + void SkeletonRenderer::setToSetupPose () { + _skeleton->setToSetupPose(); + } + void SkeletonRenderer::setBonesToSetupPose () { + _skeleton->setBonesToSetupPose(); + } + void SkeletonRenderer::setSlotsToSetupPose () { + _skeleton->setSlotsToSetupPose(); + } + + Bone* SkeletonRenderer::findBone (const std::string& boneName) const { + return _skeleton->findBone(boneName.c_str()); + } + + Slot* SkeletonRenderer::findSlot (const std::string& slotName) const { + return _skeleton->findSlot( slotName.c_str()); + } + + void SkeletonRenderer::setSkin (const std::string& skinName) { + _skeleton->setSkin(skinName.empty() ? 0 : skinName.c_str()); + } + void SkeletonRenderer::setSkin (const char* skinName) { + _skeleton->setSkin(skinName); + } + + Attachment* SkeletonRenderer::getAttachment (const std::string& slotName, const std::string& attachmentName) const { + return _skeleton->getAttachment(slotName.c_str(), attachmentName.c_str()); + } + bool SkeletonRenderer::setAttachment (const std::string& slotName, const std::string& attachmentName) { + return _skeleton->getAttachment(slotName.c_str(), attachmentName.empty() ? 0 : attachmentName.c_str()) ? true : false; + } + bool SkeletonRenderer::setAttachment (const std::string& slotName, const char* attachmentName) { + return _skeleton->getAttachment(slotName.c_str(), attachmentName) ? true : false; } - drawNode->draw(renderer, transform, transformFlags); -#if !defined(USE_MATRIX_STACK_PROJECTION_ONLY) - director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); -#endif -} + void SkeletonRenderer::setTwoColorTint(bool enabled) { + setupGLProgramState(enabled); + } -cocos2d::Rect SkeletonRenderer::getBoundingBox () const { - return _boundingRect; -} - -// --- Convenience methods for Skeleton_* functions. - -void SkeletonRenderer::updateWorldTransform() { - _skeleton->updateWorldTransform(); -} - -void SkeletonRenderer::setToSetupPose () { - _skeleton->setToSetupPose(); -} -void SkeletonRenderer::setBonesToSetupPose () { - _skeleton->setBonesToSetupPose(); -} -void SkeletonRenderer::setSlotsToSetupPose () { - _skeleton->setSlotsToSetupPose(); -} - -Bone* SkeletonRenderer::findBone (const std::string& boneName) const { - return _skeleton->findBone(boneName.c_str()); -} - -Slot* SkeletonRenderer::findSlot (const std::string& slotName) const { - return _skeleton->findSlot( slotName.c_str()); -} - -void SkeletonRenderer::setSkin (const std::string& skinName) { - _skeleton->setSkin(skinName.empty() ? 0 : skinName.c_str()); -} -void SkeletonRenderer::setSkin (const char* skinName) { - _skeleton->setSkin(skinName); -} - -Attachment* SkeletonRenderer::getAttachment (const std::string& slotName, const std::string& attachmentName) const { - return _skeleton->getAttachment(slotName.c_str(), attachmentName.c_str()); -} -bool SkeletonRenderer::setAttachment (const std::string& slotName, const std::string& attachmentName) { - return _skeleton->getAttachment(slotName.c_str(), attachmentName.empty() ? 0 : attachmentName.c_str()) ? true : false; -} -bool SkeletonRenderer::setAttachment (const std::string& slotName, const char* attachmentName) { - return _skeleton->getAttachment(slotName.c_str(), attachmentName) ? true : false; -} + bool SkeletonRenderer::isTwoColorTint() { + return getGLProgramState() == SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState(); + } -void SkeletonRenderer::setTwoColorTint(bool enabled) { - setupGLProgramState(enabled); -} - -bool SkeletonRenderer::isTwoColorTint() { - return getGLProgramState() == SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState(); -} + void SkeletonRenderer::setVertexEffect(VertexEffect *effect) { + this->_effect = effect; + } -void SkeletonRenderer::setVertexEffect(VertexEffect *effect) { - this->_effect = effect; -} + void SkeletonRenderer::setSlotsRange(int startSlotIndex, int endSlotIndex) { + _startSlotIndex = startSlotIndex == -1 ? 0 : startSlotIndex; + _endSlotIndex = endSlotIndex == -1 ? std::numeric_limits::max() : endSlotIndex; + } + + Skeleton* SkeletonRenderer::getSkeleton () const { + return _skeleton; + } + + void SkeletonRenderer::setTimeScale (float scale) { + _timeScale = scale; + } + float SkeletonRenderer::getTimeScale () const { + return _timeScale; + } + + void SkeletonRenderer::setDebugSlotsEnabled (bool enabled) { + _debugSlots = enabled; + } + bool SkeletonRenderer::getDebugSlotsEnabled () const { + return _debugSlots; + } + + void SkeletonRenderer::setDebugBonesEnabled (bool enabled) { + _debugBones = enabled; + } + bool SkeletonRenderer::getDebugBonesEnabled () const { + return _debugBones; + } -void SkeletonRenderer::setSlotsRange(int startSlotIndex, int endSlotIndex) { - _startSlotIndex = startSlotIndex == -1 ? 0 : startSlotIndex; - _endSlotIndex = endSlotIndex == -1 ? std::numeric_limits::max() : endSlotIndex; -} - -Skeleton* SkeletonRenderer::getSkeleton () const { - return _skeleton; -} - -void SkeletonRenderer::setTimeScale (float scale) { - _timeScale = scale; -} -float SkeletonRenderer::getTimeScale () const { - return _timeScale; -} - -void SkeletonRenderer::setDebugSlotsEnabled (bool enabled) { - _debugSlots = enabled; -} -bool SkeletonRenderer::getDebugSlotsEnabled () const { - return _debugSlots; -} - -void SkeletonRenderer::setDebugBonesEnabled (bool enabled) { - _debugBones = enabled; -} -bool SkeletonRenderer::getDebugBonesEnabled () const { - return _debugBones; -} - -void SkeletonRenderer::setDebugMeshesEnabled (bool enabled) { - _debugMeshes = enabled; -} -bool SkeletonRenderer::getDebugMeshesEnabled () const { - return _debugMeshes; -} - -void SkeletonRenderer::setDebugBoundingRectEnabled(bool enabled) { - _debugBoundingRect = enabled; -} - -bool SkeletonRenderer::getDebugBoundingRectEnabled() const { - return _debugBoundingRect; -} - -void SkeletonRenderer::onEnter () { -#if CC_ENABLE_SCRIPT_BINDING - if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter)) return; -#endif - Node::onEnter(); - scheduleUpdate(); -} - -void SkeletonRenderer::onExit () { -#if CC_ENABLE_SCRIPT_BINDING - if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit)) return; -#endif - Node::onExit(); - unscheduleUpdate(); -} - -// --- CCBlendProtocol - -const BlendFunc& SkeletonRenderer::getBlendFunc () const { - return _blendFunc; -} - -void SkeletonRenderer::setBlendFunc (const BlendFunc &blendFunc) { - _blendFunc = blendFunc; -} - -void SkeletonRenderer::setOpacityModifyRGB (bool value) { - _premultipliedAlpha = value; -} - -bool SkeletonRenderer::isOpacityModifyRGB () const { - return _premultipliedAlpha; -} - -namespace { - cocos2d::Rect computeBoundingRect(const float* coords, int vertexCount) - { - assert(coords); - assert(vertexCount > 0); - - const float* v = coords; - float minX = v[0]; - float minY = v[1]; - float maxX = minX; - float maxY = minY; - for (int i = 1; i < vertexCount; ++i) - { - v += 2; - float x = v[0]; - float y = v[1]; - minX = std::min(minX, x); - minY = std::min(minY, y); - maxX = std::max(maxX, x); - maxY = std::max(maxY, y); - } - return { minX, minY, maxX - minX, maxY - minY }; + void SkeletonRenderer::setDebugMeshesEnabled (bool enabled) { + _debugMeshes = enabled; + } + bool SkeletonRenderer::getDebugMeshesEnabled () const { + return _debugMeshes; } - bool slotIsOutRange(Slot& slot, int startSlotIndex, int endSlotIndex) - { - const int index = slot.getData().getIndex(); - return startSlotIndex > index || endSlotIndex < index; + void SkeletonRenderer::setDebugBoundingRectEnabled(bool enabled) { + _debugBoundingRect = enabled; } - int computeTotalCoordCount(Skeleton& skeleton, int startSlotIndex, int endSlotIndex) - { - int coordCount = 0; - for (size_t i = 0; i < skeleton.getSlots().size(); ++i) - { - Slot& slot = *skeleton.getSlots()[i]; - Attachment* const attachment = slot.getAttachment(); - if (!attachment) - { - continue; - } - if (slotIsOutRange(slot, startSlotIndex, endSlotIndex)) - { - continue; - } - // Early exit if slot is invisible - if (slot.getColor().a == 0) { - continue; - } - if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) - { - coordCount += 8; - } - else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) - { - MeshAttachment* const mesh = static_cast(attachment); - coordCount += mesh->getWorldVerticesLength(); - } - } - return coordCount; + bool SkeletonRenderer::getDebugBoundingRectEnabled() const { + return _debugBoundingRect; } - - void transformWorldVertices(float* dstCoord, int coordCount, Skeleton& skeleton, int startSlotIndex, int endSlotIndex) - { - float* dstPtr = dstCoord; -#ifndef NDEBUG - float* const dstEnd = dstCoord + coordCount; -#endif - for (size_t i = 0; i < skeleton.getSlots().size(); ++i) - { - /*const*/ Slot& slot = *skeleton.getDrawOrder()[i]; // match the draw order of SkeletonRenderer::Draw - Attachment* const attachment = slot.getAttachment(); - if (!attachment) - { - continue; - } - if (slotIsOutRange(slot, startSlotIndex, endSlotIndex)) - { - continue; - } - if (slot.getColor().a == 0) { - continue; - } - if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) - { - RegionAttachment* const regionAttachment = static_cast(attachment); - assert(dstPtr + 8 <= dstEnd); - regionAttachment->computeWorldVertices(slot.getBone(), dstPtr, 0, 2); - dstPtr += 8; - } - else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) - { - MeshAttachment* const mesh = static_cast(attachment); - assert(dstPtr + mesh->getWorldVerticesLength() <= dstEnd); - mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), dstPtr, 0, 2); - dstPtr += mesh->getWorldVerticesLength(); - } - } - assert(dstPtr == dstEnd); + void SkeletonRenderer::onEnter () { + #if CC_ENABLE_SCRIPT_BINDING + if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter)) return; + #endif + Node::onEnter(); + scheduleUpdate(); } - void interleaveCoordinates(float* dst, const float* src, int count, int dstStride) - { - if (dstStride == 2) + void SkeletonRenderer::onExit () { + #if CC_ENABLE_SCRIPT_BINDING + if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit)) return; + #endif + Node::onExit(); + unscheduleUpdate(); + } + + // --- CCBlendProtocol + + const BlendFunc& SkeletonRenderer::getBlendFunc () const { + return _blendFunc; + } + + void SkeletonRenderer::setBlendFunc (const BlendFunc &blendFunc) { + _blendFunc = blendFunc; + } + + void SkeletonRenderer::setOpacityModifyRGB (bool value) { + _premultipliedAlpha = value; + } + + bool SkeletonRenderer::isOpacityModifyRGB () const { + return _premultipliedAlpha; + } + + namespace { + cocos2d::Rect computeBoundingRect(const float* coords, int vertexCount) { - memcpy(dst, src, sizeof(float) * count * 2); - } - else - { - for (int i = 0; i < count; ++i) + assert(coords); + assert(vertexCount > 0); + + const float* v = coords; + float minX = v[0]; + float minY = v[1]; + float maxX = minX; + float maxY = minY; + for (int i = 1; i < vertexCount; ++i) { - dst[0] = src[0]; - dst[1] = src[1]; - dst += dstStride; - src += 2; + v += 2; + float x = v[0]; + float y = v[1]; + minX = std::min(minX, x); + minY = std::min(minY, y); + maxX = std::max(maxX, x); + maxY = std::max(maxY, y); } + return { minX, minY, maxX - minX, maxY - minY }; } - } - - BlendFunc makeBlendFunc(int blendMode, bool premultipliedAlpha) - { - BlendFunc blendFunc; - switch (blendMode) { - case BlendMode_Additive: - blendFunc.src = premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; - blendFunc.dst = GL_ONE; - break; - case BlendMode_Multiply: - blendFunc.src = GL_DST_COLOR; - blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA; - break; - case BlendMode_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; - break; - } - return blendFunc; - } - - - bool cullRectangle(const Mat4& transform, const cocos2d::Rect& rect, const Camera& camera) - { - // Compute rectangle center and half extents in local space - // TODO: Pass the bounding rectangle with this representation directly - const float halfRectWidth = rect.size.width * 0.5f; - const float halfRectHeight = rect.size.height * 0.5f; - const float l_cx = rect.origin.x + halfRectWidth; - const float l_cy = rect.origin.y + halfRectHeight; - - // Transform rectangle center to world space - const float w_cx = (l_cx * transform.m[0] + l_cy * transform.m[4]) + transform.m[12]; - const float w_cy = (l_cx * transform.m[1] + l_cy * transform.m[5]) + transform.m[13]; - - // Compute rectangle half extents in world space - const float w_ex = std::abs(halfRectWidth * transform.m[0]) + std::abs(halfRectHeight * transform.m[4]); - const float w_ey = std::abs(halfRectWidth * transform.m[1]) + std::abs(halfRectHeight * transform.m[5]); - - // Transform rectangle to clip space - const Mat4& viewMatrix = camera.getViewMatrix(); - const Mat4& projectionMatrix = camera.getProjectionMatrix(); - const float c_cx = (w_cx + viewMatrix.m[12]) * projectionMatrix.m[0]; - const float c_cy = (w_cy + viewMatrix.m[13]) * projectionMatrix.m[5]; - const float c_ex = w_ex * projectionMatrix.m[0]; - const float c_ey = w_ey * projectionMatrix.m[5]; - // The rectangle has z == 0 in world space - // cw = projectionMatrix[11] * vz = -vz = wz -viewMatrix.m[14] = -viewMatrix.m[14] - const float c_w = -viewMatrix.m[14]; // w in clip space - - // For each edge, test the rectangle corner closest to it - // If its distance to the edge is negative, the whole rectangle is outside the screen - // Note: the test is conservative and can return false positives in some cases - // The test is done in clip space [-1, +1] - // e.g. left culling <==> (c_cx + c_ex) / cw < -1 <==> (c_cx + c_ex) < -cw - - // Left - if (c_cx + c_ex < -c_w) + bool slotIsOutRange(Slot& slot, int startSlotIndex, int endSlotIndex) { - return true; + const int index = slot.getData().getIndex(); + return startSlotIndex > index || endSlotIndex < index; } - // Right - if (c_cx - c_ex > c_w) + int computeTotalCoordCount(Skeleton& skeleton, int startSlotIndex, int endSlotIndex) { - return true; + int coordCount = 0; + for (size_t i = 0; i < skeleton.getSlots().size(); ++i) + { + Slot& slot = *skeleton.getSlots()[i]; + Attachment* const attachment = slot.getAttachment(); + if (!attachment) + { + continue; + } + if (slotIsOutRange(slot, startSlotIndex, endSlotIndex)) + { + continue; + } + // Early exit if slot is invisible + if (slot.getColor().a == 0) { + continue; + } + if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) + { + coordCount += 8; + } + else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) + { + MeshAttachment* const mesh = static_cast(attachment); + coordCount += mesh->getWorldVerticesLength(); + } + } + return coordCount; } - // Bottom - if (c_cy + c_ey < -c_w) + + void transformWorldVertices(float* dstCoord, int coordCount, Skeleton& skeleton, int startSlotIndex, int endSlotIndex) { - return true; + float* dstPtr = dstCoord; + #ifndef NDEBUG + float* const dstEnd = dstCoord + coordCount; + #endif + for (size_t i = 0; i < skeleton.getSlots().size(); ++i) + { + /*const*/ Slot& slot = *skeleton.getDrawOrder()[i]; // match the draw order of SkeletonRenderer::Draw + Attachment* const attachment = slot.getAttachment(); + if (!attachment) + { + continue; + } + if (slotIsOutRange(slot, startSlotIndex, endSlotIndex)) + { + continue; + } + if (slot.getColor().a == 0) { + continue; + } + if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) + { + RegionAttachment* const regionAttachment = static_cast(attachment); + assert(dstPtr + 8 <= dstEnd); + regionAttachment->computeWorldVertices(slot.getBone(), dstPtr, 0, 2); + dstPtr += 8; + } + else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) + { + MeshAttachment* const mesh = static_cast(attachment); + assert(dstPtr + mesh->getWorldVerticesLength() <= dstEnd); + mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), dstPtr, 0, 2); + dstPtr += mesh->getWorldVerticesLength(); + } + } + assert(dstPtr == dstEnd); } - // Top - if (c_cy - c_ey > c_w) + void interleaveCoordinates(float* dst, const float* src, int count, int dstStride) { - return true; + if (dstStride == 2) + { + memcpy(dst, src, sizeof(float) * count * 2); + } + else + { + for (int i = 0; i < count; ++i) + { + dst[0] = src[0]; + dst[1] = src[1]; + dst += dstStride; + src += 2; + } + } + } - return false; + BlendFunc makeBlendFunc(int blendMode, bool premultipliedAlpha) + { + BlendFunc blendFunc; + switch (blendMode) { + case BlendMode_Additive: + blendFunc.src = premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + blendFunc.dst = GL_ONE; + break; + case BlendMode_Multiply: + blendFunc.src = GL_DST_COLOR; + blendFunc.dst = GL_ONE_MINUS_SRC_ALPHA; + break; + case BlendMode_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; + break; + } + return blendFunc; + } + + + bool cullRectangle(const Mat4& transform, const cocos2d::Rect& rect, const Camera& camera) + { + // Compute rectangle center and half extents in local space + // TODO: Pass the bounding rectangle with this representation directly + const float halfRectWidth = rect.size.width * 0.5f; + const float halfRectHeight = rect.size.height * 0.5f; + const float l_cx = rect.origin.x + halfRectWidth; + const float l_cy = rect.origin.y + halfRectHeight; + + // Transform rectangle center to world space + const float w_cx = (l_cx * transform.m[0] + l_cy * transform.m[4]) + transform.m[12]; + const float w_cy = (l_cx * transform.m[1] + l_cy * transform.m[5]) + transform.m[13]; + + // Compute rectangle half extents in world space + const float w_ex = std::abs(halfRectWidth * transform.m[0]) + std::abs(halfRectHeight * transform.m[4]); + const float w_ey = std::abs(halfRectWidth * transform.m[1]) + std::abs(halfRectHeight * transform.m[5]); + + // Transform rectangle to clip space + const Mat4& viewMatrix = camera.getViewMatrix(); + const Mat4& projectionMatrix = camera.getProjectionMatrix(); + const float c_cx = (w_cx + viewMatrix.m[12]) * projectionMatrix.m[0]; + const float c_cy = (w_cy + viewMatrix.m[13]) * projectionMatrix.m[5]; + const float c_ex = w_ex * projectionMatrix.m[0]; + const float c_ey = w_ey * projectionMatrix.m[5]; + // The rectangle has z == 0 in world space + // cw = projectionMatrix[11] * vz = -vz = wz -viewMatrix.m[14] = -viewMatrix.m[14] + const float c_w = -viewMatrix.m[14]; // w in clip space + + // For each edge, test the rectangle corner closest to it + // If its distance to the edge is negative, the whole rectangle is outside the screen + // Note: the test is conservative and can return false positives in some cases + // The test is done in clip space [-1, +1] + // e.g. left culling <==> (c_cx + c_ex) / cw < -1 <==> (c_cx + c_ex) < -cw + + // Left + if (c_cx + c_ex < -c_w) + { + return true; + } + + // Right + if (c_cx - c_ex > c_w) + { + return true; + } + + // Bottom + if (c_cy + c_ey < -c_w) + { + return true; + } + + // Top + if (c_cy - c_ey > c_w) + { + return true; + } + + return false; + } + + + Color4B ColorToColor4B(const Color& color) + { + return { (GLubyte)(color.r * 255.f), (GLubyte)(color.g * 255.f), (GLubyte)(color.b * 255.f), (GLubyte)(color.a * 255.f) }; + } } - - Color4B ColorToColor4B(const Color& color) - { - return { (GLubyte)(color.r * 255.f), (GLubyte)(color.g * 255.f), (GLubyte)(color.b * 255.f), (GLubyte)(color.a * 255.f) }; - } -} - } diff --git a/spine-cocos2dx/src/spine/SkeletonRenderer.h b/spine-cocos2dx/src/spine/SkeletonRenderer.h index 39811c045..9e2249b47 100644 --- a/spine-cocos2dx/src/spine/SkeletonRenderer.h +++ b/spine-cocos2dx/src/spine/SkeletonRenderer.h @@ -84,7 +84,7 @@ namespace spine { Slot* findSlot (const std::string& slotName) const; /* Sets the skin used to look up attachments not found in the SkeletonData defaultSkin. Attachments from the new skin are - * attached if the corresponding attachment from the old skin was attached. Returns false if the skin was not found. + * attached if the corresponding attachment from the old skin was attached. * @param skin May be empty string ("") for no skin.*/ void setSkin (const std::string& skinName); /** @param skin May be 0 for no skin.*/ @@ -109,7 +109,7 @@ namespace spine { /* Sets the range of slots that should be rendered. Use -1, -1 to clear the range */ void setSlotsRange(int startSlotIndex, int endSlotIndex); - // --- BlendProtocol + // --- BlendProtocol void setBlendFunc (const cocos2d::BlendFunc& blendFunc)override; const cocos2d::BlendFunc& getBlendFunc () const override; void setOpacityModifyRGB (bool value) override;