瀏覽代碼

Ported my fixes and optimization to spine stable

unknown 6 年之前
父節點
當前提交
c8a34cdbd5
共有 2 個文件被更改,包括 1073 次插入917 次删除
  1. 926 774
      spine-cocos2dx/src/spine/SkeletonRenderer.cpp
  2. 147 143
      spine-cocos2dx/src/spine/SkeletonRenderer.h

+ 926 - 774
spine-cocos2dx/src/spine/SkeletonRenderer.cpp

@@ -35,872 +35,1024 @@
 #include <spine/AttachmentVertices.h>
 #include <algorithm>
 
-#define INITIAL_WORLD_VERTICES_LENGTH 1000
-// Used for transforming attachments for bounding boxes & debug rendering
-static float* worldVertices = nullptr;
-static size_t worldVerticesLength = 0;
-
-void ensureWorldVerticesCapacity(size_t capacity) {
-	if (worldVerticesLength < capacity) {
-		float* newWorldVertices = new float[capacity];
-		memcpy(newWorldVertices, worldVertices, worldVerticesLength * sizeof(float));
-		delete[] worldVertices;
-		worldVertices = newWorldVertices;
-		worldVerticesLength = capacity;
-	}
-}
-
 USING_NS_CC;
-using std::min;
-using std::max;
+
 
 namespace spine {
-	
-	static Cocos2dTextureLoader textureLoader;
-	
-	void SkeletonRenderer::destroyScratchBuffers() {
-		if (worldVertices) {
-			delete[] worldVertices;
-			worldVertices = nullptr;
-			worldVerticesLength = 0;
-		}
-	}
-	
-	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;
+
+	namespace {
+	    Cocos2dTextureLoader textureLoader;
+
+	    int computeTotalCoordCount(Skeleton& skeleton, int startSlotIndex, int endSlotIndex);
+	    cocos2d::Rect computeBoundingRect(const float* coords, int vertexCount);
+	    void interleaveCoordinates(float* dst, const float* src, int vertexCount, int dstStride);
+	    BlendFunc makeBlendFunc(int blendMode, bool premultipliedAlpha);
+	    void transformWorldVertices(float* dstCoord, int coordCount, Skeleton& skeleton, int startSlotIndex, int endSlotIndex);
+		  bool cullRectangle(const Mat4& transform, const cocos2d::Rect& rect, const Camera& camera);
+		   Color4B ColorToColor4B(const Color& color);
+		  bool slotIsOutRange(Slot& slot, int startSlotIndex, int endSlotIndex);
 	}
+ 
+// C Variable length array
+#ifdef _MSC_VER
+	// VLA not supported, use _malloca
+	#define VLA(type, arr, count) \
+		type* arr = static_cast<type*>( _malloca(sizeof(type) * count) )
+	#define VLA_FREE(arr) do { _freea(arr); } while(false)
+#else
+	#define VLA(type, arr, count) \
+		type arr[count]
+   #define VLA_FREE(arr)
+#endif
+
+
+SkeletonRenderer* SkeletonRenderer::createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData) {
+	SkeletonRenderer* node = new SkeletonRenderer(skeleton, ownsSkeleton, 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::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();
+}
 	
-	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::setupGLProgramState (bool twoColorTintEnabled) {
+	if (twoColorTintEnabled) {
+		setGLProgramState(SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState());
+		return;
 	}
 	
-	void SkeletonRenderer::initialize () {
-		if (!worldVertices) {
-			worldVertices = new float[INITIAL_WORLD_VERTICES_LENGTH];
-			worldVerticesLength = INITIAL_WORLD_VERTICES_LENGTH;
+	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<RegionAttachment*>(attachment);
+		texture = static_cast<AttachmentVertices*>(regionAttachment->getRendererObject())->_texture;
+	} else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) {
+				MeshAttachment* meshAttachment = static_cast<MeshAttachment*>(attachment);
+				texture = static_cast<AttachmentVertices*>(meshAttachment->getRendererObject())->_texture;
 		}
-		
-		_clipper = new (__FILE__, __LINE__) SkeletonClipping();
-		
-		_blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
-		setOpacityModifyRGB(true);
-		
-		setupGLProgramState(false);
-		
-		_skeleton->setToSetupPose();
-		_skeleton->updateWorldTransform();
+	else {
+		continue;
 	}
-	
-	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];
-			if (!slot->getAttachment()) continue;
-			if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) {
-				RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment();
-				texture = static_cast<AttachmentVertices*>(attachment->getRendererObject())->_texture;
-			} else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) {
-				MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment();
-				texture = static_cast<AttachmentVertices*>(attachment->getRendererObject())->_texture;
-			} else {
-				continue;
-			}
-			
-			if (texture != nullptr) {
-				break;
-			}
+		if (texture != nullptr) {
+			break;
 		}
-		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;
-	}
-	
-	SkeletonRenderer::SkeletonRenderer ()
-	: _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) {
-	}
-	
-	SkeletonRenderer::SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas)
-	: _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) {
-		initWithSkeleton(skeleton, ownsSkeleton, ownsSkeletonData, ownsAtlas);
-	}
-	
-	SkeletonRenderer::SkeletonRenderer (SkeletonData *skeletonData, bool ownsSkeletonData)
-	: _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) {
-		initWithData(skeletonData, ownsSkeletonData);
 	}
+	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;
+}
+
+SkeletonRenderer::SkeletonRenderer ()
+	: _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _debugBoundingRect(false), _timeScale(1), _effect(nullptr), _startSlotIndex(0), _endSlotIndex(std::numeric_limits<int>::max()) {
+}
 	
-	SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, Atlas* atlas, float scale)
-	: _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) {
-		initWithJsonFile(skeletonDataFile, atlas, scale);
-	}
+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<int>::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<int>::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<int>::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<int>::max()) {
+	initWithJsonFile(skeletonDataFile, atlasFile, scale);
+}
+
+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();
+}
 	
-	SkeletonRenderer::SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale)
-	: _atlas(nullptr), _attachmentLoader(nullptr), _debugSlots(false), _debugBones(false), _debugMeshes(false), _timeScale(1), _effect(nullptr), _startSlotIndex(-1), _endSlotIndex(-1) {
-		initWithJsonFile(skeletonDataFile, atlasFile, scale);
-	}
+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);
+
+	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);
+
+	initialize();
+}
+
+void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) {
+	_atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader);
+	CCASSERT(_atlas, "Error reading atlas file.");
+
+	_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.");
+  
+	_ownsSkeleton = true;
+  _ownsAtlas = true;
+	setSkeletonData(skeletonData, true);
+
+	initialize();
+}
 	
-	SkeletonRenderer::~SkeletonRenderer () {
-		if (_ownsSkeletonData) delete _skeleton->getData();
-		if (_ownsSkeleton) delete _skeleton;
-		if (_ownsAtlas && _atlas) delete _atlas;
-		if (_attachmentLoader) delete _attachmentLoader;
-		delete _clipper;
-	}
+void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) {
+	_atlas = atlas;
+	_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
 	
-	void SkeletonRenderer::initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton, bool ownsSkeletonData, bool ownsAtlas) {
-		_skeleton = skeleton;
-		_ownsSkeleton = ownsSkeleton;
-		_ownsSkeletonData = ownsSkeletonData;
-		_ownsAtlas = ownsAtlas;
-		
-		initialize();
-	}
+	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);
 	
-	void SkeletonRenderer::initWithData (SkeletonData* skeletonData, bool ownsSkeletonData) {
-		_ownsSkeleton = true;
-		setSkeletonData(skeletonData, ownsSkeletonData);
-		initialize();
-	}
+	initialize();
+}
+
+void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) {
+	_atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader);
+	CCASSERT(_atlas, "Error reading atlas file.");
 	
-	void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) {
-		_atlas = atlas;
-		_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
-		
-		SkeletonJson* json = new (__FILE__, __LINE__) SkeletonJson(_attachmentLoader);
-		json->setScale(scale);
-		SkeletonData* skeletonData = json->readSkeletonDataFile(skeletonDataFile.c_str());
-		CCASSERT(skeletonData, !json->getError().isEmpty() ? json->getError().buffer() : "Error reading skeleton data.");
-		delete json;
-		
-		_ownsSkeleton = true;
-		setSkeletonData(skeletonData, true);
-		
-		initialize();
-	}
+	_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
 	
-	void SkeletonRenderer::initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) {
-		_atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader);
-		CCASSERT(_atlas, "Error reading atlas file.");
-		
-		_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
-		
-		SkeletonJson* json = new (__FILE__, __LINE__) SkeletonJson(_attachmentLoader);
-		json->setScale(scale);
-		SkeletonData* skeletonData = json->readSkeletonDataFile(skeletonDataFile.c_str());
-		CCASSERT(skeletonData, !json->getError().isEmpty() ? json->getError().buffer() : "Error reading skeleton data.");
-		delete json;
-		
-		_ownsSkeleton = true;
-		_ownsAtlas = true;
-		setSkeletonData(skeletonData, true);
-		
-		initialize();
-	}
+	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);
 	
-	void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale) {
-		_atlas = atlas;
-		_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
-		
-		SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(_attachmentLoader);
-		binary->setScale(scale);
-		SkeletonData* skeletonData = binary->readSkeletonDataFile(skeletonDataFile.c_str());
-		CCASSERT(skeletonData, !binary->getError().isEmpty() ? binary->getError().buffer() : "Error reading skeleton data.");
-		delete binary;
-		_ownsSkeleton = true;
-		setSkeletonData(skeletonData, true);
-		
-		initialize();
+	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) {
+  // Early exit if the skeleton is invisible
+  if (getDisplayedOpacity() == 0 || _skeleton->getColor().a == 0){
+			return;
 	}
-	
-	void SkeletonRenderer::initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale) {
-		_atlas = new (__FILE__, __LINE__) Atlas(atlasFile.c_str(), &textureLoader);
-		CCASSERT(_atlas, "Error reading atlas file.");
-		
-		_attachmentLoader = new (__FILE__, __LINE__) Cocos2dAtlasAttachmentLoader(_atlas);
-		
-		SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(_attachmentLoader);
-		binary->setScale(scale);
-		SkeletonData* skeletonData = binary->readSkeletonDataFile(skeletonDataFile.c_str());
-		CCASSERT(skeletonData, !binary->getError().isEmpty() ? binary->getError().buffer() : "Error reading skeleton data.");
-		delete binary;
-		_ownsSkeleton = true;
-		_ownsAtlas = true;
-		setSkeletonData(skeletonData, true);
-		
-		initialize();
+
+  const int coordCount = computeTotalCoordCount(*_skeleton, _startSlotIndex, _endSlotIndex);
+	if (coordCount == 0)
+	{
+		return;
 	}
-	
-	void SkeletonRenderer::update (float deltaTime) {
-		Node::update(deltaTime);
-		if (_ownsSkeleton) _skeleton->update(deltaTime * _timeScale);
+	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;
+  if (camera && cullRectangle(transform, brect, *camera))
+  {
+	  VLA_FREE(worldCoords);
+	  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::draw (Renderer* renderer, const Mat4& transform, uint32_t transformFlags) {
-		SkeletonBatch* batch = SkeletonBatch::getInstance();
-		SkeletonTwoColorBatch* twoColorBatch = SkeletonTwoColorBatch::getInstance();
-		bool isTwoColorTint = this->isTwoColorTint();
+	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];;
 		
-		// Early exit if the skeleton is invisible
-		if (getDisplayedOpacity() == 0 || _skeleton->getColor().a == 0){
-			return;
+		if (!slot->getAttachment()) {
+			_clipper->clipEnd(*slot);
+			continue;
 		}
+
+		if (slotIsOutRange(*slot, _startSlotIndex, _endSlotIndex)) {
+			_clipper->clipEnd(*slot);
+			continue;
+		}
+
+		cocos2d::TrianglesCommand::Triangles triangles;
+		TwoColorTriangles trianglesTwoColor;
 		
-		if (_effect) _effect->begin(*_skeleton);
-		
-		Color4F nodeColor;
-		nodeColor.r = getDisplayedColor().r / (float)255;
-		nodeColor.g = getDisplayedColor().g / (float)255;
-		nodeColor.b = getDisplayedColor().b / (float)255;
-		nodeColor.a = getDisplayedOpacity() / (float)255;
-		
-		Color4F color;
-		Color4F darkColor;
-		float darkPremultipliedAlpha = _premultipliedAlpha ? 255 : 0;
-		AttachmentVertices* attachmentVertices = nullptr;
-		TwoColorTrianglesCommand* lastTwoColorTrianglesCommand = nullptr;
-		bool inRange = _startSlotIndex != -1 || _endSlotIndex != -1 ? false : true;
-		for (int i = 0, n = _skeleton->getSlots().size(); i < n; ++i) {
-			Slot* slot = _skeleton->getDrawOrder()[i];
-			
-			if (_startSlotIndex >= 0 && _startSlotIndex == slot->getData().getIndex()) {
-				inRange = true;
-			}
-			
-			if (!inRange) {
+		if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) {
+			RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment();
+			attachmentVertices = (AttachmentVertices*)attachment->getRendererObject();
+				
+			// Early exit if attachment is invisible
+			if (attachment->getColor().a == 0) {
 				_clipper->clipEnd(*slot);
 				continue;
 			}
-			
-			if (_endSlotIndex >= 0 && _endSlotIndex == slot->getData().getIndex()) {
-				inRange = false;
-			}
-			
-			if (!slot->getAttachment()) {
-				_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<float*>(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<float*>(trianglesTwoColor.verts);
+				dstStride = sizeof(V3F_C4B_C4B_T2F) / sizeof(float);
 			}
+			// Copy world vertices to triangle vertices
+			interleaveCoordinates(dstTriangleVertices, worldCoordPtr, 4, dstStride);
+			worldCoordPtr += 8;
 			
-			// Early exit if slot is invisible
-			if (slot->getColor().a == 0) {
-				_clipper->clipEnd(*slot);
-				continue;
+			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;
 			}
-			
-			cocos2d::TrianglesCommand::Triangles triangles;
-			TwoColorTriangles trianglesTwoColor;
-			
-			if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) {
-				RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment();
-				attachmentVertices = (AttachmentVertices*)attachment->getRendererObject();
 				
-				// Early exit if attachment is invisible
-				if (attachment->getColor().a == 0) {
+			// 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;
 				}
 				
-				if (!isTwoColorTint) {
-					triangles.indices = attachmentVertices->_triangles->indices;
-					triangles.indexCount = attachmentVertices->_triangles->indexCount;
-					triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount);
-					triangles.vertCount = attachmentVertices->_triangles->vertCount;
-					memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount);
-					attachment->computeWorldVertices(slot->getBone(), (float*)triangles.verts, 0, 6);
+				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 {
-					trianglesTwoColor.indices = attachmentVertices->_triangles->indices;
-					trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount;
-					trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount);
-					trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount;
-					for (int i = 0; i < trianglesTwoColor.vertCount; i++) {
-						trianglesTwoColor.verts[i].texCoords = attachmentVertices->_triangles->verts[i].texCoords;
+					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;
 					}
-					attachment->computeWorldVertices(slot->getBone(), (float*)trianglesTwoColor.verts, 0, 7);
 				}
+			} else {
+				// Not clipping
+				
+				cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags);
 				
-				color.r = attachment->getColor().r;
-				color.g = attachment->getColor().g;
-				color.b = attachment->getColor().b;
-				color.a = attachment->getColor().a;
+				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 if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) {
-				MeshAttachment* attachment = (MeshAttachment*)slot->getAttachment();
-				attachmentVertices = (AttachmentVertices*)attachment->getRendererObject();
+		} 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);
 				
-				// Early exit if attachment is invisible
-				if (attachment->getColor().a == 0) {
+				if (_clipper->getClippedTriangles().size() == 0){
 					_clipper->clipEnd(*slot);
 					continue;
 				}
 				
-				if (!isTwoColorTint) {
-					triangles.indices = attachmentVertices->_triangles->indices;
-					triangles.indexCount = attachmentVertices->_triangles->indexCount;
-					triangles.verts = batch->allocateVertices(attachmentVertices->_triangles->vertCount);
-					triangles.vertCount = attachmentVertices->_triangles->vertCount;
-					memcpy(triangles.verts, attachmentVertices->_triangles->verts, sizeof(cocos2d::V3F_C4B_T2F) * attachmentVertices->_triangles->vertCount);
-					attachment->computeWorldVertices(*slot, 0, attachment->getWorldVerticesLength(), (float*)triangles.verts, 0, 6);
-				} else {
-					trianglesTwoColor.indices = attachmentVertices->_triangles->indices;
-					trianglesTwoColor.indexCount = attachmentVertices->_triangles->indexCount;
-					trianglesTwoColor.verts = twoColorBatch->allocateVertices(attachmentVertices->_triangles->vertCount);
-					trianglesTwoColor.vertCount = attachmentVertices->_triangles->vertCount;
-					for (int i = 0; i < trianglesTwoColor.vertCount; i++) {
-						trianglesTwoColor.verts[i].texCoords = attachmentVertices->_triangles->verts[i].texCoords;
-					}
-					attachment->computeWorldVertices(*slot, 0,  attachment->getWorldVerticesLength(), (float*)trianglesTwoColor.verts, 0, 7);
-				}
+				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());
 				
-				color.r = attachment->getColor().r;
-				color.g = attachment->getColor().g;
-				color.b = attachment->getColor().b;
-				color.a = attachment->getColor().a;
-			}
-			else if (slot->getAttachment()->getRTTI().isExactly(ClippingAttachment::rtti)) {
-				ClippingAttachment* clip = (ClippingAttachment*)slot->getAttachment();
-				_clipper->clipStart(*slot, clip);
-				continue;
-			} else {
-				_clipper->clipEnd(*slot);
-				continue;
-			}
-			
-			float alpha = nodeColor.a * _skeleton->getColor().a * slot->getColor().a * color.a * 255;
-			// skip rendering if the color of this attachment is 0
-			if (alpha == 0){
-				_clipper->clipEnd(*slot);
-				continue;
-			}
-			float multiplier = _premultipliedAlpha ? alpha : 255;
-			float red = nodeColor.r * _skeleton->getColor().r * color.r * multiplier;
-			float green = nodeColor.g * _skeleton->getColor().g * color.g * multiplier;
-			float blue = nodeColor.b * _skeleton->getColor().b * color.b * multiplier;
-			
-			color.r = red * slot->getColor().r;
-			color.g = green * slot->getColor().g;
-			color.b = blue * slot->getColor().b;
-			color.a = alpha;
-			
-			if (slot->hasDarkColor()) {
-				darkColor.r = red * slot->getDarkColor().r;
-				darkColor.g = green * slot->getDarkColor().g;
-				darkColor.b = blue * slot->getDarkColor().b;
-			} else {
-				darkColor.r = 0;
-				darkColor.g = 0;
-				darkColor.b = 0;
-			}
-			darkColor.a = darkPremultipliedAlpha;
-			
-			BlendFunc blendFunc;
-			switch (slot->getData().getBlendMode()) {
-				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;
-			}
-			
-			if (!isTwoColorTint) {
-				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() >> 1;
-					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);
-					
-					float* verts = _clipper->getClippedVertices().buffer();
-					float* uvs = _clipper->getClippedUVs().buffer();
-					if (_effect) {
-						Color light;
-						Color dark;
-						light.r = color.r / 255.0f;
-						light.g = color.g / 255.0f;
-						light.b = color.b / 255.0f;
-						light.a = color.a / 255.0f;
-						dark.r = dark.g = dark.b = dark.a = 0;
-						for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2) {
-							V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
-							Color lightCopy = light;
-							Color darkCopy = dark;
-							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, darkCopy);
-							vertex->colors.r = (GLubyte)(lightCopy.r * 255);
-							vertex->colors.g = (GLubyte)(lightCopy.g * 255);
-							vertex->colors.b = (GLubyte)(lightCopy.b * 255);
-							vertex->colors.a = (GLubyte)(lightCopy.a * 255);
-						}
-					} else {
-						for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv+=2) {
-							V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
-							vertex->vertices.x = verts[vv];
-							vertex->vertices.y = verts[vv + 1];
-							vertex->texCoords.u = uvs[vv];
-							vertex->texCoords.v = uvs[vv + 1];
-							vertex->colors.r = (GLubyte)color.r;
-							vertex->colors.g = (GLubyte)color.g;
-							vertex->colors.b = (GLubyte)color.b;
-							vertex->colors.a = (GLubyte)color.a;
-						}
+				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 {
-					cocos2d::TrianglesCommand* batchedTriangles = batch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture, _glProgramState, blendFunc, triangles, transform, transformFlags);
-					
-					if (_effect) {
-						Color light;
-						Color dark;
-						light.r = color.r / 255.0f;
-						light.g = color.g / 255.0f;
-						light.b = color.b / 255.0f;
-						light.a = color.a / 255.0f;
-						dark.r = dark.g = dark.b = dark.a = 0;
-						for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) {
-							V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
-							Color lightCopy = light;
-							Color darkCopy = dark;
-							_effect->transform(vertex->vertices.x, vertex->vertices.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy,  darkCopy);
-							vertex->colors.r = (GLubyte)(lightCopy.r * 255);
-							vertex->colors.g = (GLubyte)(lightCopy.g * 255);
-							vertex->colors.b = (GLubyte)(lightCopy.b * 255);
-							vertex->colors.a = (GLubyte)(lightCopy.a * 255);
-						}
-					} else {
-						for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) {
-							V3F_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
-							vertex->colors.r = (GLubyte)color.r;
-							vertex->colors.g = (GLubyte)color.g;
-							vertex->colors.b = (GLubyte)color.b;
-							vertex->colors.a = (GLubyte)color.a;
-						}
+					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 {
-				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() >> 1;
-					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);
-					
-					float* verts = _clipper->getClippedVertices().buffer();
-					float* uvs = _clipper->getClippedUVs().buffer();
-					
-					if (_effect) {
-						Color light;
-						Color dark;
-						light.r = color.r / 255.0f;
-						light.g = color.g / 255.0f;
-						light.b = color.b / 255.0f;
-						light.a = color.a / 255.0f;
-						dark.r = darkColor.r / 255.0f;
-						dark.g = darkColor.g / 255.0f;
-						dark.b = darkColor.b / 255.0f;
-						dark.a = darkColor.a / 255.0f;
-						for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2) {
-							V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
-							Color lightCopy = light;
-							Color darkCopy = dark;
-							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.r = (GLubyte)(lightCopy.r * 255);
-							vertex->color.g = (GLubyte)(lightCopy.g * 255);
-							vertex->color.b = (GLubyte)(lightCopy.b * 255);
-							vertex->color.a = (GLubyte)(lightCopy.a * 255);
-							vertex->color2.r = (GLubyte)(darkCopy.r * 255);
-							vertex->color2.g = (GLubyte)(darkCopy.g * 255);
-							vertex->color2.b = (GLubyte)(darkCopy.b * 255);
-							vertex->color2.a = (GLubyte)darkColor.a;
-						}
-					} else {
-						for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2) {
-							V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
-							vertex->position.x = verts[vv];
-							vertex->position.y = verts[vv + 1];
-							vertex->texCoords.u = uvs[vv];
-							vertex->texCoords.v = uvs[vv + 1];
-							vertex->color.r = (GLubyte)color.r;
-							vertex->color.g = (GLubyte)color.g;
-							vertex->color.b = (GLubyte)color.b;
-							vertex->color.a = (GLubyte)color.a;
-							vertex->color2.r = (GLubyte)darkColor.r;
-							vertex->color2.g = (GLubyte)darkColor.g;
-							vertex->color2.b = (GLubyte)darkColor.b;
-							vertex->color2.a = (GLubyte)darkColor.a;
-						}
+				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 {
-					TwoColorTrianglesCommand* batchedTriangles = lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, attachmentVertices->_texture->getName(), _glProgramState, blendFunc, trianglesTwoColor, transform, transformFlags);
-					
-					if (_effect) {
-						Color light;
-						Color dark;
-						light.r = color.r / 255.0f;
-						light.g = color.g / 255.0f;
-						light.b = color.b / 255.0f;
-						light.a = color.a / 255.0f;
-						dark.r = darkColor.r / 255.0f;
-						dark.g = darkColor.g / 255.0f;
-						dark.b = darkColor.b / 255.0f;
-						dark.a = darkColor.a / 255.0f;
-						
-						for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) {
-							V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
-							Color lightCopy = light;
-							Color darkCopy = dark;
-							_effect->transform(vertex->position.x, vertex->position.y, vertex->texCoords.u, vertex->texCoords.v, lightCopy, darkCopy);
-							vertex->color.r = (GLubyte)(lightCopy.r * 255);
-							vertex->color.g = (GLubyte)(lightCopy.g * 255);
-							vertex->color.b = (GLubyte)(lightCopy.b * 255);
-							vertex->color.a = (GLubyte)(lightCopy.a * 255);
-							vertex->color2.r = (GLubyte)(darkCopy.r * 255);
-							vertex->color2.g = (GLubyte)(darkCopy.g * 255);
-							vertex->color2.b = (GLubyte)(darkCopy.b * 255);
-							vertex->color2.a = (GLubyte)darkColor.a;
-						}
-					} else {
-						for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) {
-							V3F_C4B_C4B_T2F* vertex = batchedTriangles->getTriangles().verts + v;
-							vertex->color.r = (GLubyte)color.r;
-							vertex->color.g = (GLubyte)color.g;
-							vertex->color.b = (GLubyte)color.b;
-							vertex->color.a = (GLubyte)color.a;
-							vertex->color2.r = (GLubyte)darkColor.r;
-							vertex->color2.g = (GLubyte)darkColor.g;
-							vertex->color2.b = (GLubyte)darkColor.b;
-							vertex->color2.a = (GLubyte)darkColor.a;
-						}
+					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();
+	_clipper->clipEnd(*slot);
+	}
+	_clipper->clipEnd();
+	
+	if (lastTwoColorTrianglesCommand) {
+		Node* parent = this->getParent();
 		
-		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 {
-				cocos2d::Vector<Node*>& 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;
-						}
+		// 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<Node*>& 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) {
+			}
+			if (!sibling) {
+				lastTwoColorTrianglesCommand->setForceFlush(true);
+			} else {
+				SkeletonRenderer* siblingSkeleton = dynamic_cast<SkeletonRenderer*>(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);
-				} else {
-					SkeletonRenderer* siblingSkeleton = dynamic_cast<SkeletonRenderer*>(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 (_debugSlots || _debugBones || _debugMeshes) {
-			drawDebug(renderer, transform, transformFlags);
-		}
 	}
 	
-	void SkeletonRenderer::drawDebug (Renderer* renderer, const Mat4 &transform, uint32_t transformFlags) {
-		
-		Director* director = Director::getInstance();
-		director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
-		director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, transform);
-		
-		DrawNode* drawNode = DrawNode::create();
-		
-		if (_debugSlots) {
-			// Slots.
-			// DrawPrimitives::setDrawColor4B(0, 0, 255, 255);
-			glLineWidth(1);
-			Vec2 points[4];
-			V3F_C4B_T2F_Quad quad;
-			for (int i = 0, n = _skeleton->getSlots().size(); i < n; i++) {
-				Slot* slot = _skeleton->getDrawOrder()[i];
-				if (!slot->getAttachment() || !slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) continue;
-				RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment();
-				attachment->computeWorldVertices(slot->getBone(), worldVertices, 0, 2);
-				points[0] = Vec2(worldVertices[0], worldVertices[1]);
-				points[1] = Vec2(worldVertices[2], worldVertices[3]);
-				points[2] = Vec2(worldVertices[4], worldVertices[5]);
-				points[3] = Vec2(worldVertices[6], worldVertices[7]);
-				drawNode->drawPoly(points, 4, true, Color4F::BLUE);
-			}
-		}
-		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* attachment = (MeshAttachment*)slot->getAttachment();
-				ensureWorldVerticesCapacity(attachment->getWorldVerticesLength());
-				attachment->computeWorldVertices(*slot, 0, attachment->getWorldVerticesLength(), worldVertices, 0, 2);
-				for (int ii = 0; ii < attachment->getTriangles().size();) {
-					Vec2 v1(worldVertices + (attachment->getTriangles()[ii++] * 2));
-					Vec2 v2(worldVertices + (attachment->getTriangles()[ii++] * 2));
-					Vec2 v3(worldVertices + (attachment->getTriangles()[ii++] * 2));
-					drawNode->drawLine(v1, v2, Color4F::YELLOW);
-					drawNode->drawLine(v2, v3, Color4F::YELLOW);
-					drawNode->drawLine(v3, v1, Color4F::YELLOW);
-				}
-			}
-			
-		}
-		
-		drawNode->draw(renderer, transform, transformFlags);
-		director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
+	if (_effect) _effect->end();
+
+	if (_debugBoundingRect || _debugSlots || _debugBones || _debugMeshes) {
+		drawDebug(renderer, transform, transformFlags);
 	}
+
+	VLA_FREE(worldCoords);
+}
+
+
+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
+
+	DrawNode* drawNode = DrawNode::create();
 	
-	Rect SkeletonRenderer::getBoundingBox () const {
-		float minX = FLT_MAX, minY = FLT_MAX, maxX = -FLT_MAX, maxY = -FLT_MAX;
-		float scaleX = getScaleX(), scaleY = getScaleY();
-		for (int i = 0; i < _skeleton->getSlots().size(); ++i) {
-			Slot* slot = _skeleton->getSlots()[i];
-			if (!slot->getAttachment()) continue;
-			int verticesCount;
-			if (slot->getAttachment()->getRTTI().isExactly(RegionAttachment::rtti)) {
-				RegionAttachment* attachment = (RegionAttachment*)slot->getAttachment();
-				attachment->computeWorldVertices(slot->getBone(), worldVertices, 0, 2);
-				verticesCount = 8;
-			} else if (slot->getAttachment()->getRTTI().isExactly(MeshAttachment::rtti)) {
-				MeshAttachment* mesh = (MeshAttachment*)slot->getAttachment();
-				ensureWorldVerticesCapacity(mesh->getWorldVerticesLength());
-				mesh->computeWorldVertices(*slot, 0, mesh->getWorldVerticesLength(), worldVertices, 0, 2);
-				verticesCount = mesh->getWorldVerticesLength();
-			} else
+	// 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;
-			for (int ii = 0; ii < verticesCount; ii += 2) {
-				float x = worldVertices[ii] * scaleX, y = worldVertices[ii + 1] * scaleY;
-				minX = min(minX, x);
-				minY = min(minY, y);
-				maxX = max(maxX, x);
-				maxY = max(maxY, y);
 			}
+			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] =
+			{
+				{ worldVertices[0], worldVertices[1] },
+				{ worldVertices[2], worldVertices[3] },
+				{ worldVertices[4], worldVertices[5] },
+				{ worldVertices[6], worldVertices[7] }
+			};
+			drawNode->drawPoly(points, 4, true, Color4F::BLUE);
 		}
-		Vec2 position = getPosition();
-		if (minX == FLT_MAX) minX = minY = maxX = maxY = 0;
-		return Rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY);
-	}
-	
-	// --- 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;
-	}
-	
-	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::setSlotsRange(int startSlotIndex, int endSlotIndex) {
-		this->_startSlotIndex = startSlotIndex;
-		this->_endSlotIndex = endSlotIndex;
+	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;
+		}
 	}
 	
-	Skeleton* SkeletonRenderer::getSkeleton () {
-		return _skeleton;
+	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<MeshAttachment*>(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);
+		}
 	}
 	
-	void SkeletonRenderer::setTimeScale (float scale) {
-		_timeScale = scale;
-	}
-	float SkeletonRenderer::getTimeScale () const {
-		return _timeScale;
-	}
+	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;
+}
 	
-	void SkeletonRenderer::setDebugSlotsEnabled (bool enabled) {
-		_debugSlots = enabled;
-	}
-	bool SkeletonRenderer::getDebugSlotsEnabled () const {
-		return _debugSlots;
-	}
+void SkeletonRenderer::setTwoColorTint(bool enabled) {
+	setupGLProgramState(enabled);
+}
+
+bool SkeletonRenderer::isTwoColorTint() {
+	return getGLProgramState() == SkeletonTwoColorBatch::getInstance()->getTwoColorTintProgramState();
+}
 	
-	void SkeletonRenderer::setDebugBonesEnabled (bool enabled) {
-		_debugBones = enabled;
-	}
-	bool SkeletonRenderer::getDebugBonesEnabled () const {
-		return _debugBones;
-	}
+void SkeletonRenderer::setVertexEffect(VertexEffect *effect) {
+	this->_effect = effect;
+}
 	
-	void SkeletonRenderer::setDebugMeshesEnabled (bool enabled) {
-		_debugMeshes = enabled;
-	}
-	bool SkeletonRenderer::getDebugMeshesEnabled () const {
-		return _debugMeshes;
-	}
+void SkeletonRenderer::setSlotsRange(int startSlotIndex, int endSlotIndex) {
+	_startSlotIndex = startSlotIndex == -1 ? 0 : startSlotIndex;
+	_endSlotIndex = endSlotIndex == -1 ? std::numeric_limits<int>::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::onEnter () {
+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;
+	if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter)) return;
 #endif
-		Node::onEnter();
-		scheduleUpdate();
-	}
-	
-	void SkeletonRenderer::onExit () {
+	Node::onEnter();
+	scheduleUpdate();
+}
+
+void SkeletonRenderer::onExit () {
 #if CC_ENABLE_SCRIPT_BINDING
-		if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit)) return;
+	if (_scriptType == kScriptTypeJavascript && ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit)) return;
 #endif
-		Node::onExit();
-		unscheduleUpdate();
+	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 };
 	}
-	
-	// --- CCBlendProtocol
-	
-	const BlendFunc& SkeletonRenderer::getBlendFunc () const {
-		return _blendFunc;
+
+	bool slotIsOutRange(Slot& slot, int startSlotIndex, int endSlotIndex)
+	{
+		const int index = slot.getData().getIndex();
+		return startSlotIndex > index || endSlotIndex < index;
 	}
-	
-	void SkeletonRenderer::setBlendFunc (const BlendFunc &blendFunc) {
-		_blendFunc = blendFunc;
+
+	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;
+			}
+			if (attachment->getRTTI().isExactly(RegionAttachment::rtti))
+			{
+				coordCount += 8;
+			}
+			else if (attachment->getRTTI().isExactly(MeshAttachment::rtti))
+			{
+				MeshAttachment* const mesh = static_cast<MeshAttachment*>(attachment);
+				coordCount += mesh->getWorldVerticesLength();
+			}
+		}
+		return coordCount;
 	}
-	
-	void SkeletonRenderer::setOpacityModifyRGB (bool value) {
-		_premultipliedAlpha = value;
+
+
+	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 (attachment->getRTTI().isExactly(RegionAttachment::rtti))
+			{
+				RegionAttachment* const regionAttachment = static_cast<RegionAttachment*>(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<MeshAttachment*>(attachment);
+				assert(dstPtr + mesh->getWorldVerticesLength() <= dstEnd);
+				mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), dstPtr, 0, 2);
+				dstPtr += mesh->getWorldVerticesLength();
+			}
+		}
+		assert(dstPtr == dstEnd);
 	}
-	
-	bool SkeletonRenderer::isOpacityModifyRGB () const {
-		return _premultipliedAlpha;
+
+	void interleaveCoordinates(float* dst, const float* src, int count, int dstStride)
+	{
+		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;
+			}
+		}
+
 	}
-	
+
+	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) };
+	}
+}
+
 }

+ 147 - 143
spine-cocos2dx/src/spine/SkeletonRenderer.h

@@ -1,30 +1,31 @@
 /******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated May 1, 2019. Replaces all prior versions.
+ * Spine Runtimes Software License v2.5
  *
- * Copyright (c) 2013-2019, Esoteric Software LLC
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
  *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes or (b) remove,
+ * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
+ * or other intellectual property or proprietary rights notices on or in the
+ * Software, including any copy thereof. Redistributions in binary or source
+ * form must include this license and terms.
  *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
- * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
 #ifndef SPINE_SKELETONRENDERER_H_
@@ -34,129 +35,132 @@
 #include "cocos2d.h"
 
 namespace spine {
+
+class AttachmentVertices;
+
+/* Draws a skeleton. */
+class SkeletonRenderer: public cocos2d::Node, public cocos2d::BlendProtocol {
+public:
+	CREATE_FUNC(SkeletonRenderer);
+	static SkeletonRenderer* createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false);
+	static SkeletonRenderer* createWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false);
+	static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1);
+	static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);
+
+	void update (float deltaTime) override;
+	void draw (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags) override;
+	//virtual bool isAutoCulled () const override;
+	cocos2d::Rect getBoundingBox () const override;
+	void onEnter () override;
+	void onExit () override;
+
+	Skeleton* getSkeleton() const;
+
+	void setTimeScale(float scale);
+	float getTimeScale() const;
+
+	/*  */
+	void setDebugSlotsEnabled(bool enabled);
+	bool getDebugSlotsEnabled() const;
+
+	void setDebugBonesEnabled(bool enabled);
+	bool getDebugBonesEnabled() const;
+	
+	void setDebugMeshesEnabled(bool enabled);
+	bool getDebugMeshesEnabled() const;
+ 
+	void setDebugBoundingRectEnabled(bool enabled);
+	bool getDebugBoundingRectEnabled() const;
+
+	// --- Convenience methods for common Skeleton_* functions.
+	void updateWorldTransform ();
+
+	void setToSetupPose ();
+	void setBonesToSetupPose ();
+	void setSlotsToSetupPose ();
+
+	/* Returns 0 if the bone was not found. */
+	Bone* findBone (const std::string& boneName) const;
+	/* Returns 0 if the slot was not found. */
+	Slot* findSlot (const std::string& slotName) const;
 	
-	class AttachmentVertices;
+	/* 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.
+	 * @param skin May be empty string ("") for no skin.*/
+	void setSkin (const std::string& skinName);
+	/** @param skin May be 0 for no skin.*/
+	void setSkin (const char* skinName);
 	
-	/* Draws a skeleton. */
-	class SkeletonRenderer: public cocos2d::Node, public cocos2d::BlendProtocol {
-	public:
-		CREATE_FUNC(SkeletonRenderer);
-		static SkeletonRenderer* createWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false);
-		static SkeletonRenderer* createWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false);
-		static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1);
-		static SkeletonRenderer* createWithFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);
-		
-		virtual void update (float deltaTime) override;
-		virtual void draw (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags) override;
-		virtual void drawDebug (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags);
-		virtual cocos2d::Rect getBoundingBox () const override;
-		virtual void onEnter () override;
-		virtual void onExit () override;
-		
-		Skeleton* getSkeleton();
-		
-		void setTimeScale(float scale);
-		float getTimeScale() const;
-		
-		/*  */
-		void setDebugSlotsEnabled(bool enabled);
-		bool getDebugSlotsEnabled() const;
-		
-		void setDebugBonesEnabled(bool enabled);
-		bool getDebugBonesEnabled() const;
-		
-		void setDebugMeshesEnabled(bool enabled);
-		bool getDebugMeshesEnabled() const;
-		
-		// --- Convenience methods for common Skeleton_* functions.
-		void updateWorldTransform ();
-		
-		void setToSetupPose ();
-		void setBonesToSetupPose ();
-		void setSlotsToSetupPose ();
-		
-		/* Returns 0 if the bone was not found. */
-		Bone* findBone (const std::string& boneName) const;
-		/* Returns 0 if the slot was not found. */
-		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.
-		 * @param skin May be empty string ("") for no skin.*/
-		void setSkin (const std::string& skinName);
-		/** @param skin May be 0 for no skin.*/
-		void setSkin (const char* skinName);
-		
-		/* Returns 0 if the slot or attachment was not found. */
-		Attachment* getAttachment (const std::string& slotName, const std::string& attachmentName) const;
-		/* Returns false if the slot or attachment was not found.
-		 * @param attachmentName May be empty string ("") for no attachment. */
-		bool setAttachment (const std::string& slotName, const std::string& attachmentName);
-		/* @param attachmentName May be 0 for no attachment. */
-		bool setAttachment (const std::string& slotName, const char* attachmentName);
-		
-		/* Enables/disables two color tinting for this instance. May break batching */
-		void setTwoColorTint(bool enabled);
-		/* Whether two color tinting is enabled */
-		bool isTwoColorTint();
-		
-		/* Sets the vertex effect to be used, set to 0 to disable vertex effects */
-		void setVertexEffect(VertexEffect* effect);
-		
-		/* Sets the range of slots that should be rendered. Use -1, -1 to clear the range */
-		void setSlotsRange(int startSlotIndex, int endSlotIndex);
-		
-		// --- BlendProtocol
-		virtual void setBlendFunc (const cocos2d::BlendFunc& blendFunc)override;
-		virtual const cocos2d::BlendFunc& getBlendFunc () const override;
-		virtual void setOpacityModifyRGB (bool value) override;
-		virtual bool isOpacityModifyRGB () const override;
-		
-		// Frees global memory used for temporay vertex transformations.
-		static void destroyScratchBuffers();
-		
-	CC_CONSTRUCTOR_ACCESS:
-		SkeletonRenderer ();
-		SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false, bool ownsAtlas = false);
-		SkeletonRenderer (SkeletonData* skeletonData, bool ownsSkeletonData = false);
-		SkeletonRenderer (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1);
-		SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);
-		
-		virtual ~SkeletonRenderer ();
-		
-		void initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false, bool ownsAtlas = false);
-		void initWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false);
-		void initWithJsonFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1);
-		void initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);
-		void initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1);
-		void initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);
-		
-		virtual void initialize ();
-		
-	protected:
-		void setSkeletonData (SkeletonData* skeletonData, bool ownsSkeletonData);
-		void setupGLProgramState(bool twoColorTintEnabled);
-		
-		bool _ownsSkeletonData;
-		bool _ownsSkeleton;
-		bool _ownsAtlas;
-		Atlas* _atlas;
-		AttachmentLoader* _attachmentLoader;
-		cocos2d::CustomCommand _debugCommand;
-		cocos2d::BlendFunc _blendFunc;
-		bool _premultipliedAlpha;
-		Skeleton* _skeleton;
-		float _timeScale;
-		bool _debugSlots;
-		bool _debugBones;
-		bool _debugMeshes;
-		SkeletonClipping* _clipper;
-		VertexEffect* _effect;
-		
-		int _startSlotIndex;
-		int _endSlotIndex;
-	};
+	/* Returns 0 if the slot or attachment was not found. */
+	Attachment* getAttachment (const std::string& slotName, const std::string& attachmentName) const;
+	/* Returns false if the slot or attachment was not found.
+	 * @param attachmentName May be empty string ("") for no attachment. */
+	bool setAttachment (const std::string& slotName, const std::string& attachmentName);
+	/* @param attachmentName May be 0 for no attachment. */
+	bool setAttachment (const std::string& slotName, const char* attachmentName);
 	
+	/* Enables/disables two color tinting for this instance. May break batching */
+	void setTwoColorTint(bool enabled);
+	/* Whether two color tinting is enabled */
+	bool isTwoColorTint();
+	
+	/* Sets the vertex effect to be used, set to 0 to disable vertex effects */
+	void setVertexEffect(VertexEffect* effect);
+	
+	/* Sets the range of slots that should be rendered. Use -1, -1 to clear the range */
+	void setSlotsRange(int startSlotIndex, int endSlotIndex);
+
+    // --- BlendProtocol
+	void setBlendFunc (const cocos2d::BlendFunc& blendFunc)override;
+	const cocos2d::BlendFunc& getBlendFunc () const override;
+	void setOpacityModifyRGB (bool value) override;
+	bool isOpacityModifyRGB () const override;
+	
+CC_CONSTRUCTOR_ACCESS:
+	SkeletonRenderer ();
+	SkeletonRenderer(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false, bool ownsAtlas = false);
+	SkeletonRenderer (SkeletonData* skeletonData, bool ownsSkeletonData = false);
+	SkeletonRenderer (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1);
+	SkeletonRenderer (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);
+
+	virtual ~SkeletonRenderer ();
+
+	void initWithSkeleton(Skeleton* skeleton, bool ownsSkeleton = false, bool ownsSkeletonData = false, bool ownsAtlas = false);
+	void initWithData (SkeletonData* skeletonData, bool ownsSkeletonData = false);
+	void initWithJsonFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1);
+	void initWithJsonFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);
+	void initWithBinaryFile (const std::string& skeletonDataFile, Atlas* atlas, float scale = 1);
+	void initWithBinaryFile (const std::string& skeletonDataFile, const std::string& atlasFile, float scale = 1);
+
+	virtual void initialize ();
+	
+protected:
+	void setSkeletonData (SkeletonData* skeletonData, bool ownsSkeletonData);
+	void setupGLProgramState(bool twoColorTintEnabled);
+	virtual void drawDebug (cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformFlags);
+
+	bool _ownsSkeletonData;
+	bool _ownsSkeleton;
+  bool _ownsAtlas = false;
+	Atlas* _atlas;
+	AttachmentLoader* _attachmentLoader;
+	cocos2d::CustomCommand _debugCommand;
+	cocos2d::BlendFunc _blendFunc;
+	bool _premultipliedAlpha;
+	Skeleton* _skeleton;
+	float _timeScale;
+	bool _debugSlots;
+	bool _debugBones;
+	bool _debugMeshes;
+	bool _debugBoundingRect;
+	SkeletonClipping* _clipper;
+	VertexEffect* _effect;
+	cocos2d::Rect _boundingRect;
+	
+	int _startSlotIndex;
+	int _endSlotIndex;
+};
+
 }
 
 #endif /* SPINE_SKELETONRENDERER_H_ */