Browse Source

[cocos2d-objc] More two color tinting work, shader appears broken

badlogic 8 years ago
parent
commit
c746da940f

+ 13 - 0
spine-cocos2d-objc/TwoColorTest.atlas

@@ -0,0 +1,13 @@
+
+TwoColorTest.png
+size: 512,512
+format: RGBA8888
+filter: Linear,Linear
+repeat: none
+squareWithBorder
+  rotate: false
+  xy: 2, 2
+  size: 300, 300
+  orig: 300, 300
+  offset: 0, 0
+  index: -1

+ 150 - 0
spine-cocos2d-objc/TwoColorTest.json

@@ -0,0 +1,150 @@
+{
+"skeleton": { "hash": "+PGAjM+dGgrId6XMqdIJm8hlvRE", "spine": "3.6.09-beta", "width": 300, "height": 300, "images": "" },
+"bones": [
+	{ "name": "root", "x": -178.03, "y": 229.83 },
+	{ "name": "singleColorTint", "parent": "root", "x": -400 },
+	{ "name": "singleColorTint2", "parent": "root", "x": -400, "y": -383 },
+	{ "name": "twoColorTint", "parent": "root", "x": 800 },
+	{ "name": "twoColorTint2", "parent": "root", "x": 800, "y": -382 },
+	{ "name": "twoColorTint (blackOnly)", "parent": "root" },
+	{ "name": "twoColorTint (blackOnly)2", "parent": "root", "y": -391 },
+	{ "name": "twoColorTint (colorOnly)", "parent": "root", "x": 400 },
+	{ "name": "twoColorTint (colorOnly)2", "parent": "root", "x": 400, "y": -382 }
+],
+"slots": [
+	{ "name": "squareWithBorder", "bone": "singleColorTint", "attachment": "squareWithBorder" },
+	{ "name": "squareWithBorder7", "bone": "singleColorTint2", "attachment": "squareWithBorder" },
+	{ "name": "squareWithBorder2", "bone": "twoColorTint (blackOnly)", "dark": "000000", "attachment": "squareWithBorder" },
+	{ "name": "squareWithBorder8", "bone": "twoColorTint (blackOnly)2", "dark": "000000", "attachment": "squareWithBorder" },
+	{ "name": "squareWithBorder4", "bone": "twoColorTint (colorOnly)", "dark": "000000", "attachment": "squareWithBorder" },
+	{ "name": "squareWithBorder5", "bone": "twoColorTint (colorOnly)2", "dark": "000000", "attachment": "squareWithBorder" },
+	{ "name": "squareWithBorder3", "bone": "twoColorTint", "dark": "000000", "attachment": "squareWithBorder" },
+	{ "name": "squareWithBorder6", "bone": "twoColorTint2", "dark": "000000", "attachment": "squareWithBorder" }
+],
+"skins": {
+	"default": {
+		"squareWithBorder": {
+			"squareWithBorder": { "width": 300, "height": 300 }
+		},
+		"squareWithBorder2": {
+			"squareWithBorder": { "width": 300, "height": 300 }
+		},
+		"squareWithBorder3": {
+			"squareWithBorder": { "width": 300, "height": 300 }
+		},
+		"squareWithBorder4": {
+			"squareWithBorder": { "width": 300, "height": 300 }
+		},
+		"squareWithBorder5": {
+			"squareWithBorder": {
+				"type": "mesh",
+				"uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+				"triangles": [ 1, 2, 3, 1, 3, 0 ],
+				"vertices": [ 150, -150, -150, -150, -197, 99, 183, 155 ],
+				"hull": 4,
+				"edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+				"width": 300,
+				"height": 300
+			}
+		},
+		"squareWithBorder6": {
+			"squareWithBorder": {
+				"type": "mesh",
+				"uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+				"triangles": [ 1, 2, 3, 1, 3, 0 ],
+				"vertices": [ 238, -200, -191, -60, -150, 150, 119, 111 ],
+				"hull": 4,
+				"edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+				"width": 300,
+				"height": 300
+			}
+		},
+		"squareWithBorder7": {
+			"squareWithBorder": {
+				"type": "mesh",
+				"uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+				"triangles": [ 1, 2, 3, 1, 3, 0 ],
+				"vertices": [ 210, -132, -150, -150, -150, 150, 124, 119 ],
+				"hull": 4,
+				"edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+				"width": 300,
+				"height": 300
+			}
+		},
+		"squareWithBorder8": {
+			"squareWithBorder": {
+				"type": "mesh",
+				"uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+				"triangles": [ 1, 2, 3, 1, 3, 0 ],
+				"vertices": [ 150, -150, -150, -150, -97, 58, 86, 62 ],
+				"hull": 4,
+				"edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+				"width": 300,
+				"height": 300
+			}
+		}
+	}
+},
+"animations": {
+	"animation": {
+		"slots": {
+			"squareWithBorder": {
+				"color": [
+					{ "time": 0, "color": "fffffffe" },
+					{ "time": 1, "color": "9e17b3fe" },
+					{ "time": 2, "color": "fffffffe" }
+				]
+			},
+			"squareWithBorder2": {
+				"twoColor": [
+					{ "time": 0, "light": "fffffffe", "dark": "000000" },
+					{ "time": 1, "light": "fffffffe", "dark": "ff0000" },
+					{ "time": 2, "light": "fffffffe", "dark": "000000" }
+				]
+			},
+			"squareWithBorder3": {
+				"twoColor": [
+					{ "time": 0, "light": "fffffffe", "dark": "000000" },
+					{ "time": 1, "light": "80ff00fe", "dark": "001cff" },
+					{ "time": 2, "light": "fffffffe", "dark": "000000" }
+				]
+			},
+			"squareWithBorder4": {
+				"twoColor": [
+					{ "time": 0, "light": "fffffffe", "dark": "000000" },
+					{ "time": 1, "light": "ffd300fe", "dark": "000000" },
+					{ "time": 2, "light": "fffffffe", "dark": "000000" }
+				]
+			},
+			"squareWithBorder5": {
+				"twoColor": [
+					{ "time": 0, "light": "fffffffe", "dark": "000000" },
+					{ "time": 1, "light": "ffd300fe", "dark": "000000" },
+					{ "time": 2, "light": "fffffffe", "dark": "000000" }
+				]
+			},
+			"squareWithBorder6": {
+				"twoColor": [
+					{ "time": 0, "light": "fffffffe", "dark": "000000" },
+					{ "time": 1, "light": "80ff00fe", "dark": "001cff" },
+					{ "time": 2, "light": "fffffffe", "dark": "000000" }
+				]
+			},
+			"squareWithBorder7": {
+				"color": [
+					{ "time": 0, "color": "fffffffe" },
+					{ "time": 1, "color": "9e17b3fe" },
+					{ "time": 2, "color": "fffffffe" }
+				]
+			},
+			"squareWithBorder8": {
+				"twoColor": [
+					{ "time": 0, "light": "fffffffe", "dark": "000000" },
+					{ "time": 1, "light": "fffffffe", "dark": "ff0000" },
+					{ "time": 2, "light": "fffffffe", "dark": "000000" }
+				]
+			}
+		}
+	}
+}
+}

BIN
spine-cocos2d-objc/TwoColorTest.png


+ 6 - 15
spine-cocos2d-objc/example/SpineboyExample.m

@@ -36,6 +36,7 @@
 + (CCScene*) scene {
 	CCScene *scene = [CCScene node];
 	[scene addChild:[SpineboyExample node]];
+	[scene setColorRGBA: [CCColor colorWithRed:1 green:0 blue:0]];
 	return scene;
 }
 
@@ -43,11 +44,12 @@
 	self = [super init];
 	if (!self) return nil;
 
-	skeletonNode = [SkeletonAnimation skeletonWithFile:@"spineboy.json" atlasFile:@"spineboy.atlas" scale:0.6];
+	skeletonNode = [SkeletonAnimation skeletonWithFile:@"TwoColorTest.json" atlasFile:@"TwoColorTest.atlas" scale:0.2];
 	[skeletonNode setMixFrom:@"walk" to:@"jump" duration:0.2f];
 	[skeletonNode setMixFrom:@"jump" to:@"run" duration:0.2f];
 
     __weak SkeletonAnimation* node = skeletonNode;
+	skeletonNode.twoColorTint = true;
 	skeletonNode.startListener = ^(spTrackEntry* entry) {
 		const char* animationName = entry->animation->name;
 		NSLog(@"%d start: %s", entry->trackIndex, animationName);
@@ -68,18 +70,12 @@
 		NSLog(@"%d event: %s, %d, %f, %s", entry->trackIndex, event->data->name, event->intValue, event->floatValue, event->stringValue);
 	};
 
-	[skeletonNode setAnimationForTrack:0 name:@"walk" loop:YES];
-	spTrackEntry* jumpEntry = [skeletonNode addAnimationForTrack:0 name:@"jump" loop:NO afterDelay:3];
-	[skeletonNode addAnimationForTrack:0 name:@"run" loop:YES afterDelay:0];
-
-	[skeletonNode setListenerForEntry:jumpEntry onStart:^(spTrackEntry* entry) {
-		CCLOG(@"jumped!");
-	}];
+	[skeletonNode setAnimationForTrack:0 name:@"animation" loop:YES];
 
 	// [skeletonNode setAnimationForTrack:1 name:@"test" loop:YES];
 
 	CGSize windowSize = [[CCDirector sharedDirector] viewSize];
-	[skeletonNode setPosition:ccp(windowSize.width / 2, 20)];
+	[skeletonNode setPosition:ccp(windowSize.width / 2, windowSize.height / 2)];
 	[self addChild:skeletonNode];
 
 	self.userInteractionEnabled = YES;
@@ -90,12 +86,7 @@
 
 #if ( TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR )
 - (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
-	if (!skeletonNode.debugBones)
-		skeletonNode.debugBones = true;
-	else if (skeletonNode.timeScale == 1)
-		skeletonNode.timeScale = 0.3f;
-	else
-		[[CCDirector sharedDirector] replaceScene:[GoblinsExample scene]];
+	skeletonNode.twoColorTint = !skeletonNode.twoColorTint;
 }
 #endif
 

+ 12 - 0
spine-cocos2d-objc/spine-cocos2d-objc.xcodeproj/project.pbxproj

@@ -34,6 +34,9 @@
 		765A2EF61D7D7A08003FB779 /* goblins.atlas in Resources */ = {isa = PBXBuildFile; fileRef = 765A2EF41D7D7A08003FB779 /* goblins.atlas */; };
 		765A2EF71D7D7A08003FB779 /* goblins.png in Resources */ = {isa = PBXBuildFile; fileRef = 765A2EF51D7D7A08003FB779 /* goblins.png */; };
 		76BF7E071E66ED9C00485998 /* GLUtils.c in Sources */ = {isa = PBXBuildFile; fileRef = 76BF7E051E66ED9C00485998 /* GLUtils.c */; };
+		76BFBC301E78254F00675E2B /* TwoColorTest.atlas in Resources */ = {isa = PBXBuildFile; fileRef = 76BFBC2D1E78254F00675E2B /* TwoColorTest.atlas */; };
+		76BFBC311E78254F00675E2B /* TwoColorTest.json in Resources */ = {isa = PBXBuildFile; fileRef = 76BFBC2E1E78254F00675E2B /* TwoColorTest.json */; };
+		76BFBC321E78254F00675E2B /* TwoColorTest.png in Resources */ = {isa = PBXBuildFile; fileRef = 76BFBC2F1E78254F00675E2B /* TwoColorTest.png */; };
 		76F28D161DEC810300CDE54D /* Animation.c in Sources */ = {isa = PBXBuildFile; fileRef = 76F28CF41DEC810200CDE54D /* Animation.c */; };
 		76F28D171DEC810300CDE54D /* AnimationState.c in Sources */ = {isa = PBXBuildFile; fileRef = 76F28CF51DEC810300CDE54D /* AnimationState.c */; };
 		76F28D181DEC810300CDE54D /* AnimationStateData.c in Sources */ = {isa = PBXBuildFile; fileRef = 76F28CF61DEC810300CDE54D /* AnimationStateData.c */; };
@@ -162,6 +165,9 @@
 		765A2EF51D7D7A08003FB779 /* goblins.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = goblins.png; path = Resources/goblins.png; sourceTree = "<group>"; };
 		76BF7E051E66ED9C00485998 /* GLUtils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = GLUtils.c; path = src/spine/GLUtils.c; sourceTree = "<group>"; };
 		76BF7E061E66ED9C00485998 /* GLUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GLUtils.h; path = src/spine/GLUtils.h; sourceTree = "<group>"; };
+		76BFBC2D1E78254F00675E2B /* TwoColorTest.atlas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TwoColorTest.atlas; sourceTree = "<group>"; };
+		76BFBC2E1E78254F00675E2B /* TwoColorTest.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = TwoColorTest.json; sourceTree = "<group>"; };
+		76BFBC2F1E78254F00675E2B /* TwoColorTest.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TwoColorTest.png; sourceTree = "<group>"; };
 		76F28CF41DEC810200CDE54D /* Animation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Animation.c; path = "../spine-c/spine-c/src/spine/Animation.c"; sourceTree = "<group>"; };
 		76F28CF51DEC810300CDE54D /* AnimationState.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AnimationState.c; path = "../spine-c/spine-c/src/spine/AnimationState.c"; sourceTree = "<group>"; };
 		76F28CF61DEC810300CDE54D /* AnimationStateData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AnimationStateData.c; path = "../spine-c/spine-c/src/spine/AnimationStateData.c"; sourceTree = "<group>"; };
@@ -338,6 +344,9 @@
 		43C32867170B0C7F004A9460 /* Resources */ = {
 			isa = PBXGroup;
 			children = (
+				76BFBC2D1E78254F00675E2B /* TwoColorTest.atlas */,
+				76BFBC2E1E78254F00675E2B /* TwoColorTest.json */,
+				76BFBC2F1E78254F00675E2B /* TwoColorTest.png */,
 				765A2EF41D7D7A08003FB779 /* goblins.atlas */,
 				765A2EF51D7D7A08003FB779 /* goblins.png */,
 				76F5BD9C1D2BDE1C005917E5 /* raptor.atlas */,
@@ -517,14 +526,17 @@
 				43C3287D170B0DBE004A9460 /* [email protected] in Resources */,
 				43C3287E170B0DBE004A9460 /* Default-Landscape~ipad.png in Resources */,
 				43C3287F170B0DBE004A9460 /* Default.png in Resources */,
+				76BFBC321E78254F00675E2B /* TwoColorTest.png in Resources */,
 				76F5BDA31D2BDE1C005917E5 /* raptor.json in Resources */,
 				43C32880170B0DBE004A9460 /* [email protected] in Resources */,
 				765A2EF71D7D7A08003FB779 /* goblins.png in Resources */,
 				43C32881170B0DBE004A9460 /* Icon-72.png in Resources */,
 				76F5BDA41D2BDE1C005917E5 /* raptor.png in Resources */,
 				43C32882170B0DBE004A9460 /* Icon-Small-50.png in Resources */,
+				76BFBC301E78254F00675E2B /* TwoColorTest.atlas in Resources */,
 				76F5BDA21D2BDE1C005917E5 /* raptor.atlas in Resources */,
 				43C32883170B0DBE004A9460 /* Icon-Small.png in Resources */,
+				76BFBC311E78254F00675E2B /* TwoColorTest.json in Resources */,
 				43C32884170B0DBE004A9460 /* [email protected] in Resources */,
 				43C32885170B0DBE004A9460 /* Icon.png in Resources */,
 				76F5BDA71D2BDE1C005917E5 /* tank.png in Resources */,

+ 68 - 6
spine-cocos2d-objc/src/spine/GLUtils.c

@@ -52,8 +52,6 @@ attribute vec4 a_position;
 attribute vec4 a_color;
 attribute vec4 a_color2;
 attribute vec2 a_texCoords;
-													 
-uniform mat4 transform;
 
 \n#ifdef GL_ES\n
 varying lowp vec4 v_light;
@@ -63,14 +61,13 @@ varying mediump vec2 v_texCoord;
 varying vec4 v_light;
 varying vec4 v_dark;
 varying vec2 v_texCoord;
-
 \n#endif\n
 
 void main() {
 	v_light = a_color;
 	v_dark = a_color2;
 	v_texCoord = a_texCoords;
-	gl_Position = transform * a_position;
+	gl_Position = a_position;
 }
 );
 
@@ -89,7 +86,7 @@ void main() {
    vec4 texColor = texture2D(texture, v_texCoord);
    float alpha = texColor.a * v_light.a;
    gl_FragColor.a = alpha;
-   gl_FragColor.rgb = (1.0 - texColor.rgb) * v_dark.rgb * alpha + texColor.rgb * v_light.rgb;	
+   gl_FragColor.rgb = (1.0 - texColor.rgb) * v_dark.rgb * alpha + texColor.rgb * v_light.rgb;
 }
 );
 
@@ -119,6 +116,10 @@ void spMesh_allocatePart(spMesh* mesh, spMeshPart* part, uint32_t numVertices, u
 	part->numIndices = numIndices;
 	part->startIndex = mesh->numAllocatedIndices;
 	part->numVertices = numVertices;
+	part->textureHandle = textureHandle;
+	part->srcBlend = srcBlend;
+	part->dstBlend = dstBlend;
+	
 	mesh->numAllocatedVertices += numVertices;
 	mesh->numAllocatedIndices += numIndices;
 }
@@ -194,6 +195,7 @@ spTwoColorBatcher* spTwoColorBatcher_create() {
 	batcher->colorAttributeLocation = glGetAttribLocation(batcher->shader->program, "a_color");
 	batcher->color2AttributeLocation = glGetAttribLocation(batcher->shader->program, "a_color2");
 	batcher->texCoordsAttributeLocation = glGetAttribLocation(batcher->shader->program, "a_texCoords");
+	batcher->textureUniformLocation = glGetUniformLocation(batcher->shader->program, "texture");
 	
 	glGenBuffers(1, &batcher->vertexBufferHandle);
 	glGenBuffers(1, &batcher->indexBufferHandle);
@@ -204,12 +206,72 @@ spTwoColorBatcher* spTwoColorBatcher_create() {
 	return batcher;
 }
 
-void spTwoColorBatcher_add(spTwoColorBatcher* batcher, spMeshPart* mesh) {
+void spTwoColorBatcher_add(spTwoColorBatcher* batcher, spMeshPart mesh) {
+	if (batcher->numVertices + mesh.numVertices > MAX_VERTICES || batcher->numIndices + mesh.numIndices > MAX_INDICES) {
+		spTwoColorBatcher_flush(batcher);
+	}
+	
+	if (batcher->lastTextureHandle != mesh.textureHandle || batcher->lastSrcBlend != mesh.srcBlend || batcher->lastDstBlend != mesh.dstBlend) {
+		spTwoColorBatcher_flush(batcher);
+	}
 	
+	spVertex* vertices = &batcher->verticesBuffer[batcher->numVertices];
+	unsigned short* indices = &batcher->indicesBuffer[batcher->numIndices];
+	
+	memcpy(vertices, &mesh.mesh->vertices[mesh.startVertex], mesh.numVertices * sizeof(spVertex));
+	unsigned short offset = (unsigned short)batcher->numVertices;
+	for (int i = batcher->numIndices, j = mesh.startIndex, n = batcher->numIndices + mesh.numIndices; i < n; i++, j++) {
+		indices[i] = mesh.mesh->indices[j] + offset;
+	}
+	
+	batcher->numIndices += mesh.numIndices;
+	batcher->numVertices += mesh.numVertices;
+	batcher->lastSrcBlend = mesh.srcBlend;
+	batcher->lastDstBlend = mesh.dstBlend;
+	batcher->lastTextureHandle = mesh.textureHandle;
 }
 
 void spTwoColorBatcher_flush(spTwoColorBatcher* batcher) {
+	if (batcher->numVertices == 0 || batcher->numIndices == 0)
+		return;
+	
+	glUseProgram(batcher->shader->program);
+		
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_2D, batcher->lastTextureHandle);
+	glUniform1i(batcher->textureUniformLocation, 0);
+	
+	glBlendFunc(batcher->lastSrcBlend, batcher->lastDstBlend);
 	
+	glBindBuffer(GL_ARRAY_BUFFER, batcher->vertexBufferHandle);
+	glBufferData(GL_ARRAY_BUFFER, sizeof(spVertex) * batcher->numVertices , batcher->verticesBuffer, GL_DYNAMIC_DRAW);
+	
+	glEnableVertexAttribArray(batcher->positionAttributeLocation);
+	glEnableVertexAttribArray(batcher->colorAttributeLocation);
+	glEnableVertexAttribArray(batcher->color2AttributeLocation);
+	glEnableVertexAttribArray(batcher->texCoordsAttributeLocation);
+	
+	glVertexAttribPointer(batcher->positionAttributeLocation, 4, GL_FLOAT, GL_FALSE, sizeof(spVertex), (GLvoid*)0);
+	glVertexAttribPointer(batcher->colorAttributeLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(spVertex), (GLvoid*)16);
+	glVertexAttribPointer(batcher->color2AttributeLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(spVertex), (GLvoid*)20);
+	glVertexAttribPointer(batcher->texCoordsAttributeLocation, 2, GL_FLOAT, GL_FALSE, sizeof(spVertex), (GLvoid*)24);
+	
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batcher->indexBufferHandle);
+	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * batcher->numIndices, batcher->indicesBuffer, GL_STATIC_DRAW);
+	
+	glDrawElements(GL_TRIANGLES, (GLsizei)batcher->numIndices, GL_UNSIGNED_SHORT, 0);
+	
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+	
+	glUseProgram(0);
+	glBindTexture(GL_TEXTURE_2D, 0);
+	
+	batcher->numIndices = 0;
+	batcher->numVertices = 0;
+	batcher->lastSrcBlend = -1;
+	batcher->lastDstBlend = -1;
+	batcher->lastTextureHandle = -1;
 }
 
 void spDisposeTwoColorBatcher(spTwoColorBatcher* batcher) {

+ 6 - 1
spine-cocos2d-objc/src/spine/GLUtils.h

@@ -89,10 +89,15 @@ typedef struct spTwoColorBatcher {
 	int32_t colorAttributeLocation;
 	int32_t color2AttributeLocation;
 	int32_t texCoordsAttributeLocation;
+	int32_t textureUniformLocation;
+	
+	uint32_t lastTextureHandle;
+	uint32_t lastSrcBlend;
+	uint32_t lastDstBlend;
 } spTwoColorBatcher;
 
 spTwoColorBatcher* spTwoColorBatcher_create();
-void spTwoColorBatcher_add(spTwoColorBatcher* batcher, spMeshPart* meshPart);
+void spTwoColorBatcher_add(spTwoColorBatcher* batcher, spMeshPart meshPart);
 void spTwoColorBatcher_flush(spTwoColorBatcher* batcher);
 void spDisposeTwoColorBatcher(spTwoColorBatcher* batcher);
 

+ 44 - 4
spine-cocos2d-objc/src/spine/SkeletonRenderer.m

@@ -37,6 +37,7 @@
 static const unsigned short quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 static spTwoColorBatcher* batcher = 0;
 static spMesh* mesh = 0;
+static bool handlerQueued = false;
 
 @interface SkeletonRenderer (Private)
 - (void) initialize:(spSkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData;
@@ -66,9 +67,6 @@ static spMesh* mesh = 0;
 	if (!batcher) {
 		batcher = spTwoColorBatcher_create();
 		mesh = spMesh_create(64000, 32000);
-		[[CCDirector sharedDirector] addFrameCompletionHandler: ^{
-			printf ("frame completed");
-		}];
 	}
 	
 	_ownsSkeletonData = ownsSkeletonData;
@@ -159,6 +157,20 @@ static spMesh* mesh = 0;
 }
 
 -(void)draw:(CCRenderer *)renderer transform:(const GLKMatrix4 *)transform {
+	// FIXME we need to clear the mesh parts at the end of the frame
+	// there's no general event mechanism to get notified on end frame
+	// that doesn't need to be re-added every frame. This is a poor man
+	// notification system that may break if the block is called on a
+	// separate thread.
+	if (!handlerQueued) {
+		[[CCDirector sharedDirector] addFrameCompletionHandler: ^{
+			printf("clearing mesh\n");
+			spMesh_clearParts(mesh);
+			handlerQueued = false;
+		}];
+		handlerQueued = true;
+	}
+	
 	CCColor* nodeColor = self.color;
 	_skeleton->color.r = nodeColor.red;
 	_skeleton->color.g = nodeColor.green;
@@ -265,13 +277,41 @@ static spMesh* mesh = 0;
 					spMeshPart meshPart;
 					spMesh_allocatePart(mesh, &meshPart, verticesCount / 2, trianglesCount, self.texture.name, srcBlend, dstBlend);
 					
+					spVertex* vertices = &meshPart.mesh->vertices[meshPart.startVertex];
+					unsigned short* indices = &meshPart.mesh->indices[meshPart.startIndex];
+					
+					for (int i = 0; i * 2 < verticesCount; i++, vertices++) {
+						CCVertex vertex;
+						vertex.position = GLKVector4Make(_worldVertices[i * 2], _worldVertices[i * 2 + 1], 0.0, 1.0);
+						vertex = CCVertexApplyTransform(vertex, transform);
+						vertices->x = vertex.position.x;
+						vertices->y = vertex.position.y;
+						vertices->z = vertex.position.z;
+						vertices->w = vertex.position.w;
+						vertices->color = ((int)(r * 255)) << 24 | ((int)(g * 255)) << 16 | ((int)(b * 255)) << 8 | ((int)(a * 255));
+						vertices->color2 = ((int)(r * 255)) << 24 | ((int)(g * 255)) << 16 | ((int)(b * 255)) << 8 | ((int)(a * 255));
+						vertices->u = uvs[i * 2];
+						vertices->v = 1 - uvs[i * 2 + 1];
+					}
+					
+					for (int j = 0; j < trianglesCount; j++, indices++) {
+						*indices = triangles[j];
+					}
+					
 					[renderer enqueueBlock:^{
-						
+						spTwoColorBatcher_add(batcher, meshPart);
 					} globalSortOrder:0 debugLabel: nil threadSafe: false];
 				}
 			}
 		}
 	}
+	
+	if (self.twoColorTint) {
+		[renderer enqueueBlock:^{
+			spTwoColorBatcher_flush(batcher);
+		} globalSortOrder:0 debugLabel: nil threadSafe: false];
+	}
+	
 	[_drawNode clear];
 	if (_debugSlots) {
 		// Slots.