Quellcode durchsuchen

Blend modes for all runtimes.

NathanSweet vor 10 Jahren
Ursprung
Commit
f8a76c6455
38 geänderte Dateien mit 386 neuen und 132 gelöschten Zeilen
  1. 1 1
      spine-as3/spine-as3-example/src/spineboy.json
  2. 46 0
      spine-as3/spine-as3/src/spine/BlendMode.as
  3. 1 1
      spine-as3/spine-as3/src/spine/SkeletonJson.as
  4. 1 1
      spine-as3/spine-as3/src/spine/SlotData.as
  5. 3 1
      spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as
  6. 1 1
      spine-c/include/spine/Slot.h
  7. 11 2
      spine-c/include/spine/SlotData.h
  8. 13 5
      spine-c/src/spine/SkeletonJson.c
  9. 16 4
      spine-cocos2d-iphone/2/src/spine/SkeletonRenderer.m
  10. 3 2
      spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.h
  11. 47 29
      spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.m
  12. 16 4
      spine-cocos2dx/2/src/spine/SkeletonRenderer.cpp
  13. 16 4
      spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp
  14. 10 1
      spine-corona/spine-corona/spine.lua
  15. 1 0
      spine-csharp/spine-csharp.csproj
  16. 1 0
      spine-csharp/spine-csharp_xna.csproj
  17. 35 0
      spine-csharp/src/BlendMode.cs
  18. 1 1
      spine-csharp/src/SkeletonBinary.cs
  19. 4 2
      spine-csharp/src/SkeletonJson.cs
  20. 2 2
      spine-csharp/src/SlotData.cs
  21. 9 2
      spine-js/spine.js
  22. 30 0
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BlendMode.java
  23. 2 2
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java
  24. 1 3
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java
  25. 14 26
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java
  26. 5 5
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java
  27. 3 1
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java
  28. 8 3
      spine-love/spine-love/spine.lua
  29. 37 0
      spine-lua/BlendMode.lua
  30. 2 1
      spine-lua/SkeletonJson.lua
  31. 3 1
      spine-lua/SlotData.lua
  32. 19 8
      spine-sfml/src/spine/spine-sfml.cpp
  33. 14 8
      spine-starling/spine-starling/src/spine/starling/PolygonBatch.as
  34. 4 2
      spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as
  35. 1 1
      spine-turbulenz/example/index.html
  36. 3 6
      spine-unity/Assets/spine-unity/SkeletonRenderer.cs
  37. 1 1
      spine-xna/src/SkeletonMeshRenderer.cs
  38. 1 1
      spine-xna/src/SkeletonRegionRenderer.cs

+ 1 - 1
spine-as3/spine-as3-example/src/spineboy.json

@@ -54,7 +54,7 @@
 	{ "name": "goggles", "bone": "head", "attachment": "goggles" },
 	{ "name": "front_bracer", "bone": "front_bracer", "attachment": "front_bracer" },
 	{ "name": "front_fist", "bone": "front_fist", "attachment": "front_fist_closed" },
-	{ "name": "muzzle", "bone": "gunTip", "additive": true },
+	{ "name": "muzzle", "bone": "gunTip", "blend": "additive" },
 	{ "name": "head-bb", "bone": "head" }
 ],
 "skins": {

+ 46 - 0
spine-as3/spine-as3/src/spine/BlendMode.as

@@ -0,0 +1,46 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * or (b) remove, delete, alter or obscure any trademarks or any copyright,
+ * trademark, patent or other intellectual property or proprietary rights
+ * notices on or in the Software, including any copy thereof. Redistributions
+ * in binary or source form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+package spine {
+
+public class BlendMode {
+	public static const normal:BlendMode = new BlendMode(0);
+	public static const additive:BlendMode = new BlendMode(1);
+	public static const multiply:BlendMode = new BlendMode(2);
+	public static const screen:BlendMode = new BlendMode(3);
+
+	public var ordinal:int;
+
+	public function BlendMode (ordinal:int) {
+		this.ordinal = ordinal;
+	}
+}
+
+}

+ 1 - 1
spine-as3/spine-as3/src/spine/SkeletonJson.as

@@ -145,7 +145,7 @@ public class SkeletonJson {
 			}
 
 			slotData.attachmentName = slotMap["attachment"];
-			slotData.additiveBlending = slotMap["additive"];
+			slotData.blendMode = BlendMode[slotMap["blend"] || "normal"];
 
 			skeletonData.slots[skeletonData.slots.length] = slotData;
 		}

+ 1 - 1
spine-as3/spine-as3/src/spine/SlotData.as

@@ -38,7 +38,7 @@ public class SlotData {
 	public var b:Number = 1;
 	public var a:Number = 1;
 	public var attachmentName:String;
-	public var additiveBlending:Boolean;
+	public var blendMode:BlendMode;
 
 	public function SlotData (name:String, boneData:BoneData) {
 		if (name == null) throw new ArgumentError("name cannot be null.");

+ 3 - 1
spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as

@@ -52,6 +52,8 @@ import spine.attachments.RegionAttachment;
 public class SkeletonSprite extends Sprite {
 	static private var tempPoint:Point = new Point();
 	static private var tempMatrix:Matrix = new Matrix();
+	static private var blendModes:Vector.<String> = new <String>[
+		BlendMode.NORMAL, BlendMode.ADD, BlendMode.MULTIPLY, BlendMode.SCREEN];
 
 	private var _skeleton:Skeleton;
 	public var timeScale:Number = 1;
@@ -119,7 +121,7 @@ public class SkeletonSprite extends Sprite {
 					regionAttachment["wrapper"] = wrapper;
 				}
 
-				wrapper.blendMode = slot.data.additiveBlending ? BlendMode.ADD : BlendMode.NORMAL;
+				wrapper.blendMode = blendModes[slot.data.blendMode.ordinal];
 
 				var colorTransform:ColorTransform = wrapper.transform.colorTransform;
 				colorTransform.redMultiplier = skeleton.r * slot.r * regionAttachment.r;

+ 1 - 1
spine-c/include/spine/Slot.h

@@ -53,7 +53,7 @@ typedef struct spSlot {
 	spSlot() :
 		data(0),
 		bone(0),
-		r(0), b(0), g(0), a(0),
+		r(0), g(0), b(0), a(0),
 		attachment(0),
 		attachmentVerticesCapacity(0),
 		attachmentVerticesCount(0),

+ 11 - 2
spine-c/include/spine/SlotData.h

@@ -37,12 +37,16 @@
 extern "C" {
 #endif
 
+typedef enum {
+	SP_BLEND_MODE_NORMAL, SP_BLEND_MODE_ADDITIVE, SP_BLEND_MODE_MULTIPLY, SP_BLEND_MODE_SCREEN
+} spBlendMode;
+
 typedef struct spSlotData {
 	const char* const name;
 	const spBoneData* const boneData;
 	const char* attachmentName;
 	float r, g, b, a;
-	int/*bool*/additiveBlending;
+	spBlendMode blendMode;
 
 #ifdef __cplusplus
 	spSlotData() :
@@ -50,7 +54,7 @@ typedef struct spSlotData {
 		boneData(0),
 		attachmentName(0),
 		r(0), g(0), b(0), a(0),
-		additiveBlending(0) {
+		blendMode(SP_BLEND_MODE_NORMAL) {
 	}
 #endif
 } spSlotData;
@@ -62,6 +66,11 @@ void spSlotData_dispose (spSlotData* self);
 void spSlotData_setAttachmentName (spSlotData* self, const char* attachmentName);
 
 #ifdef SPINE_SHORT_NAMES
+typedef spBlendMode BlendMode;
+#define BLEND_MODE_NORMAL SP_BLEND_MODE_NORMAL
+#define BLEND_MODE_ADDITIVE SP_BLEND_MODE_ADDITIVE
+#define BLEND_MODE_MULTIPLY SP_BLEND_MODE_MULTIPLY
+#define BLEND_MODE_SCREEN SP_BLEND_MODE_SCREEN
 typedef spSlotData SlotData;
 #define SlotData_create(...) spSlotData_create(__VA_ARGS__)
 #define SlotData_dispose(...) spSlotData_dispose(__VA_ARGS__)

+ 13 - 5
spine-c/src/spine/SkeletonJson.c

@@ -519,7 +519,7 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 		for (slotMap = slots->child, i = 0; slotMap; slotMap = slotMap->next, ++i) {
 			spSlotData* slotData;
 			const char* color;
-			Json *attachmentItem;
+			Json *item;
 
 			const char* boneName = Json_getString(slotMap, "bone", 0);
 			spBoneData* boneData = spSkeletonData_findBone(skeletonData, boneName);
@@ -539,10 +539,18 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 				slotData->a = toColor(color, 3);
 			}
 
-			attachmentItem = Json_getItem(slotMap, "attachment");
-			if (attachmentItem) spSlotData_setAttachmentName(slotData, attachmentItem->valueString);
-
-			slotData->additiveBlending = Json_getInt(slotMap, "additive", 0);
+			item = Json_getItem(slotMap, "attachment");
+			if (item) spSlotData_setAttachmentName(slotData, item->valueString);
+
+			item = Json_getItem(slotMap, "blend");
+			if (item) {
+				if (strcmp(item->valueString, "additive") == 0)
+					slotData->blendMode = SP_BLEND_MODE_ADDITIVE;
+				else if (strcmp(item->valueString, "multiply") == 0)
+					slotData->blendMode = SP_BLEND_MODE_MULTIPLY;
+				else if (strcmp(item->valueString, "screen") == 0)
+					slotData->blendMode = SP_BLEND_MODE_SCREEN;
+			}
 
 			skeletonData->slots[i] = slotData;
 		}

+ 16 - 4
spine-cocos2d-iphone/2/src/spine/SkeletonRenderer.m

@@ -142,7 +142,7 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 	_skeleton->b = nodeColor.b / (float)255;
 	_skeleton->a = self.opacity / (float)255;
 
-	int additive = -1;
+	int blendMode = -1;
 	ccColor4B color;
 	const float* uvs = 0;
 	int verticesCount = 0;
@@ -199,10 +199,22 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 		default: ;
 		}
 		if (texture) {
-			if (slot->data->additiveBlending != additive) {
+			if (slot->data->blendMode != blendMode) {
 				[batch flush];
-				ccGLBlendFunc(_blendFunc.src, slot->data->additiveBlending ? GL_ONE : _blendFunc.dst);
-				additive = slot->data->additiveBlending;
+				blendMode = slot->data->blendMode;
+				switch (slot->data->blendMode) {
+				case SP_BLEND_MODE_ADDITIVE:
+					ccGLBlendFunc(_premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE);
+					break;
+				case SP_BLEND_MODE_MULTIPLY:
+					ccGLBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
+					break;
+				case SP_BLEND_MODE_SCREEN:
+					ccGLBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
+					break;
+				default:
+					ccGLBlendFunc(_premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+				}
 			}
 			color.a = _skeleton->a * slot->a * a * 255;
 			float multiplier = _premultipliedAlpha ? color.a : 255;

+ 3 - 2
spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.h

@@ -39,10 +39,11 @@
 	bool _debugBones;
 	bool _premultipliedAlpha;
 	ccBlendFunc _blendFunc;
-    CCDrawNode *_drawNode;
+	CCDrawNode* _drawNode;
 	bool _ownsSkeletonData;
 	spAtlas* _atlas;
-	float* worldVertices;
+	float* _worldVertices;
+	CCBlendMode* screenMode;
 }
 
 + (id) skeletonWithData:(spSkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData;

+ 47 - 29
spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.m

@@ -62,7 +62,7 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 - (void) initialize:(spSkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData {
 	_ownsSkeletonData = ownsSkeletonData;
 
-	worldVertices = MALLOC(float, 1000); // Max number of vertices per mesh.
+	_worldVertices = MALLOC(float, 1000); // Max number of vertices per mesh.
 
 	_skeleton = spSkeleton_create(skeletonData);
 	_rootBone = _skeleton->bones[0];
@@ -74,6 +74,12 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 	[self addChild:_drawNode];
 	
 	[self setShader:[CCShader positionTextureColorShader]];
+
+	_premultipliedAlpha = true;
+	screenMode = [CCBlendMode blendModeWithOptions:@{
+		CCBlendFuncSrcColor: @(GL_ONE),
+		CCBlendFuncDstColor: @(GL_ONE_MINUS_SRC_COLOR)}
+	];
 }
 
 - (id) initWithData:(spSkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData {
@@ -127,7 +133,7 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 	if (_ownsSkeletonData) spSkeletonData_dispose(_skeleton->data);
 	if (_atlas) spAtlas_dispose(_atlas);
 	spSkeleton_dispose(_skeleton);
-	FREE(worldVertices);
+	FREE(_worldVertices);
 	[super dealloc];
 }
 
@@ -138,7 +144,7 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 	_skeleton->b = nodeColor.blue;
 	_skeleton->a = self.displayedOpacity;
 
-	int additive = -1;
+	int blendMode = -1;
 	const float* uvs = 0;
 	int verticesCount = 0;
 	const int* triangles = 0;
@@ -151,7 +157,7 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 		switch (slot->attachment->type) {
 		case SP_ATTACHMENT_REGION: {
 			spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
-			spRegionAttachment_computeWorldVertices(attachment, slot->bone, worldVertices);
+			spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
 			texture = [self getTextureForRegion:attachment];
 			uvs = attachment->uvs;
 			verticesCount = 8;
@@ -165,7 +171,7 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 		}
 		case SP_ATTACHMENT_MESH: {
 			spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment;
-			spMeshAttachment_computeWorldVertices(attachment, slot, worldVertices);
+			spMeshAttachment_computeWorldVertices(attachment, slot, _worldVertices);
 			texture = [self getTextureForMesh:attachment];
 			uvs = attachment->uvs;
 			verticesCount = attachment->verticesCount;
@@ -179,7 +185,7 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 		}
 		case SP_ATTACHMENT_SKINNED_MESH: {
 			spSkinnedMeshAttachment* attachment = (spSkinnedMeshAttachment*)slot->attachment;
-			spSkinnedMeshAttachment_computeWorldVertices(attachment, slot, worldVertices);
+			spSkinnedMeshAttachment_computeWorldVertices(attachment, slot, _worldVertices);
 			texture = [self getTextureForSkinnedMesh:attachment];
 			uvs = attachment->uvs;
 			verticesCount = attachment->uvsCount;
@@ -194,22 +200,34 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 		default: ;
 		}
 		if (texture) {
-			if (slot->data->additiveBlending != additive) {
-				[self setBlendMode:[CCBlendMode blendModeWithOptions:@{CCBlendFuncSrcColor: @(_blendFunc.src),CCBlendFuncDstColor: @(slot->data->additiveBlending ? GL_ONE : _blendFunc.dst)}]];
-				additive = slot->data->additiveBlending;
+			if (slot->data->blendMode != blendMode) {
+				blendMode = slot->data->blendMode;
+				switch (slot->data->blendMode) {
+				case SP_BLEND_MODE_ADDITIVE:
+					[self setBlendMode:[CCBlendMode addMode]];
+					break;
+				case SP_BLEND_MODE_MULTIPLY:
+					[self setBlendMode:[CCBlendMode multiplyMode]];
+					break;
+				case SP_BLEND_MODE_SCREEN:
+					[self setBlendMode:screenMode];
+					break;
+				default:
+					[self setBlendMode:_premultipliedAlpha ? [CCBlendMode premultipliedAlphaMode] : [CCBlendMode alphaMode]];
+				}
 			}
 			if (_premultipliedAlpha) {
-                a *= _skeleton->a * slot->a;
-                r *= _skeleton->r * slot->r * a;
-                g *= _skeleton->g * slot->g * a;
-                b *= _skeleton->b * slot->b * a;
-            } else {
-                a *= _skeleton->a * slot->a;
-                r *= _skeleton->r * slot->r;
-                g *= _skeleton->g * slot->g;
-                b *= _skeleton->b * slot->b;
+				a *= _skeleton->a * slot->a;
+				r *= _skeleton->r * slot->r * a;
+				g *= _skeleton->g * slot->g * a;
+				b *= _skeleton->b * slot->b * a;
+			} else {
+				a *= _skeleton->a * slot->a;
+				r *= _skeleton->r * slot->r;
+				g *= _skeleton->g * slot->g;
+				b *= _skeleton->b * slot->b;
 			}
-            self.texture = texture;
+			self.texture = texture;
 			CGSize size = texture.contentSize;
 			GLKVector2 center = GLKVector2Make(size.width / 2.0, size.height / 2.0);
 			GLKVector2 extents = GLKVector2Make(size.width / 2.0, size.height / 2.0);
@@ -217,7 +235,7 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 				CCRenderBuffer buffer = [renderer enqueueTriangles:(trianglesCount / 3) andVertexes:verticesCount withState:self.renderState globalSortOrder:0];
 				for (int i = 0; i * 2 < verticesCount; ++i) {
 					CCVertex vertex;
-					vertex.position = GLKVector4Make(worldVertices[i * 2], worldVertices[i * 2 + 1], 0.0, 1.0);
+					vertex.position = GLKVector4Make(_worldVertices[i * 2], _worldVertices[i * 2 + 1], 0.0, 1.0);
 					vertex.color = GLKVector4Make(r, g, b, a);
 					vertex.texCoord1 = GLKVector2Make(uvs[i * 2], 1 - uvs[i * 2 + 1]);
 					CCRenderBufferSetVertex(buffer, i, CCVertexApplyTransform(vertex, transform));
@@ -236,11 +254,11 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 			spSlot* slot = _skeleton->drawOrder[i];
 			if (!slot->attachment || slot->attachment->type != SP_ATTACHMENT_REGION) continue;
 			spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
-			spRegionAttachment_computeWorldVertices(attachment, slot->bone, worldVertices);
-			points[0] = ccp(worldVertices[0], worldVertices[1]);
-			points[1] = ccp(worldVertices[2], worldVertices[3]);
-			points[2] = ccp(worldVertices[4], worldVertices[5]);
-			points[3] = ccp(worldVertices[6], worldVertices[7]);
+			spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
+			points[0] = ccp(_worldVertices[0], _worldVertices[1]);
+			points[1] = ccp(_worldVertices[2], _worldVertices[3]);
+			points[2] = ccp(_worldVertices[4], _worldVertices[5]);
+			points[3] = ccp(_worldVertices[6], _worldVertices[7]);
 			[_drawNode drawPolyWithVerts:points count:4 fillColor:[CCColor clearColor] borderWidth:1 borderColor:[CCColor blueColor]];
 		}
 	}
@@ -283,20 +301,20 @@ static const int quadTriangles[6] = {0, 1, 2, 2, 3, 0};
 		int verticesCount;
 		if (slot->attachment->type == SP_ATTACHMENT_REGION) {
 			spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
-			spRegionAttachment_computeWorldVertices(attachment, slot->bone, worldVertices);
+			spRegionAttachment_computeWorldVertices(attachment, slot->bone, _worldVertices);
 			verticesCount = 8;
 		} else if (slot->attachment->type == SP_ATTACHMENT_MESH) {
 			spMeshAttachment* mesh = (spMeshAttachment*)slot->attachment;
-			spMeshAttachment_computeWorldVertices(mesh, slot, worldVertices);
+			spMeshAttachment_computeWorldVertices(mesh, slot, _worldVertices);
 			verticesCount = mesh->verticesCount;
 		} else if (slot->attachment->type == SP_ATTACHMENT_SKINNED_MESH) {
 			spSkinnedMeshAttachment* mesh = (spSkinnedMeshAttachment*)slot->attachment;
-			spSkinnedMeshAttachment_computeWorldVertices(mesh, slot, worldVertices);
+			spSkinnedMeshAttachment_computeWorldVertices(mesh, slot, _worldVertices);
 			verticesCount = mesh->uvsCount;
 		} else
 			continue;
 		for (int ii = 0; ii < verticesCount; ii += 2) {
-			float x = worldVertices[ii] * scaleX, y = worldVertices[ii + 1] * scaleY;
+			float x = _worldVertices[ii] * scaleX, y = _worldVertices[ii + 1] * scaleY;
 			minX = fmin(minX, x);
 			minY = fmin(minY, y);
 			maxX = fmax(maxX, x);

+ 16 - 4
spine-cocos2dx/2/src/spine/SkeletonRenderer.cpp

@@ -144,7 +144,7 @@ void SkeletonRenderer::draw () {
 	skeleton->b = nodeColor.b / (float)255;
 	skeleton->a = getDisplayedOpacity() / (float)255;
 
-	int additive = -1;
+	int blendMode = -1;
 	ccColor4B color;
 	const float* uvs = nullptr;
 	int verticesCount = 0;
@@ -200,10 +200,22 @@ void SkeletonRenderer::draw () {
 		}
 		}
 		if (texture) {
-			if (slot->data->additiveBlending != additive) {
+			if (slot->data->blendMode != blendMode) {
 				batch->flush();
-				ccGLBlendFunc(blendFunc.src, slot->data->additiveBlending ? GL_ONE : blendFunc.dst);
-				additive = slot->data->additiveBlending;
+				blendMode = slot->data->blendMode;
+				switch (slot->data->blendMode) {
+				case SP_BLEND_MODE_ADDITIVE:
+					ccGLBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE);
+					break;
+				case SP_BLEND_MODE_MULTIPLY:
+					ccGLBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
+					break;
+				case SP_BLEND_MODE_SCREEN:
+					ccGLBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
+					break;
+				default:
+					ccGLBlendFunc(blendFunc.src, blendFunc.dst);
+				}
 			}
 			color.a = skeleton->a * slot->a * a * 255;
 			float multiplier = premultipliedAlpha ? color.a : 255;

+ 16 - 4
spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp

@@ -158,7 +158,7 @@ void SkeletonRenderer::drawSkeleton (const Mat4 &transform, uint32_t transformFl
 	_skeleton->b = nodeColor.b / (float)255;
 	_skeleton->a = getDisplayedOpacity() / (float)255;
 
-	int additive = -1;
+	int blendMode = -1;
 	Color4B color;
 	const float* uvs = nullptr;
 	int verticesCount = 0;
@@ -215,10 +215,22 @@ void SkeletonRenderer::drawSkeleton (const Mat4 &transform, uint32_t transformFl
 		default: ;
 		} 
 		if (texture) {
-			if (slot->data->additiveBlending != additive) {
+			if (slot->data->blendMode != blendMode) {
 				_batch->flush();
-				GL::blendFunc(_blendFunc.src, slot->data->additiveBlending ? GL_ONE : _blendFunc.dst);
-				additive = slot->data->additiveBlending;
+				blendMode = slot->data->blendMode;
+				switch (slot->data->blendMode) {
+				case SP_BLEND_MODE_ADDITIVE:
+					GL::blendFunc(_premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE);
+					break;
+				case SP_BLEND_MODE_MULTIPLY:
+					GL::blendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
+					break;
+				case SP_BLEND_MODE_SCREEN:
+					GL::blendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
+					break;
+				default:
+					GL::blendFunc(_blendFunc.src, _blendFunc.dst);
+				}
 			}
 			color.a = _skeleton->a * slot->a * a * 255;
 			float multiplier = _premultipliedAlpha ? color.a : 255;

+ 10 - 1
spine-corona/spine-corona/spine.lua

@@ -52,6 +52,7 @@ spine.AnimationState = require "spine-lua.AnimationState"
 spine.EventData = require "spine-lua.EventData"
 spine.Event = require "spine-lua.Event"
 spine.SkeletonBounds = require "spine-lua.SkeletonBounds"
+spine.BlendMode = require "spine-lua.BlendMode"
 
 spine.utils.readFile = function (fileName, base)
 	if not base then base = system.ResourceDirectory end
@@ -125,7 +126,15 @@ function spine.Skeleton.new (skeletonData, group)
 						print("Error creating image: " .. attachment.name)
 						image = spine.Skeleton.failed
 					end
-					if slot.data.additiveBlending then image.blendMode = "add" end
+					if slot.data.blendMode == spine.BlendMode.normal then
+						image.blendMode = "normal"
+					elseif slot.data.blendMode == spine.BlendMode.additive then
+						image.blendMode = "add"
+					elseif slot.data.blendMode == spine.BlendMode.multiply then
+						image.blendMode = "multiply"
+					elseif slot.data.blendMode == spine.BlendMode.screen then
+						image.blendMode = "screen"
+					end
 					images[slot] = image
 				end
 				-- Position image based on attachment and bone.

+ 1 - 0
spine-csharp/spine-csharp.csproj

@@ -64,6 +64,7 @@
     <Compile Include="src\Attachments\MeshAttachment.cs" />
     <Compile Include="src\Attachments\RegionAttachment.cs" />
     <Compile Include="src\Attachments\SkinnedMeshAttachment.cs" />
+    <Compile Include="src\BlendMode.cs" />
     <Compile Include="src\Bone.cs" />
     <Compile Include="src\BoneData.cs" />
     <Compile Include="src\AnimationState.cs" />

+ 1 - 0
spine-csharp/spine-csharp_xna.csproj

@@ -103,6 +103,7 @@
     <Compile Include="src\Attachments\AttachmentType.cs" />
     <Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
     <Compile Include="src\Attachments\RegionAttachment.cs" />
+    <Compile Include="src\BlendMode.cs" />
     <Compile Include="src\Bone.cs" />
     <Compile Include="src\BoneData.cs" />
     <Compile Include="src\IkConstraint.cs" />

+ 35 - 0
spine-csharp/src/BlendMode.cs

@@ -0,0 +1,35 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * or (b) remove, delete, alter or obscure any trademarks or any copyright,
+ * trademark, patent or other intellectual property or proprietary rights
+ * notices on or in the Software, including any copy thereof. Redistributions
+ * in binary or source form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+namespace Spine {
+	public enum BlendMode {
+		normal, additive, multiply, screen
+	}
+}

+ 1 - 1
spine-csharp/src/SkeletonBinary.cs

@@ -156,7 +156,7 @@ namespace Spine {
 				slotData.b = ((color & 0x0000ff00) >> 8) / 255f;
 				slotData.a = ((color & 0x000000ff)) / 255f;
 				slotData.attachmentName = ReadString(input);
-				slotData.additiveBlending = ReadBoolean(input);
+				slotData.blendMode = (BlendMode)ReadInt(input, true);
 				skeletonData.slots.Add(slotData);
 			}
 

+ 4 - 2
spine-csharp/src/SkeletonJson.cs

@@ -163,8 +163,10 @@ namespace Spine {
 					if (slotMap.ContainsKey("attachment"))
 						slotData.attachmentName = (String)slotMap["attachment"];
 
-					if (slotMap.ContainsKey("additive"))
-						slotData.additiveBlending = (bool)slotMap["additive"];
+					if (slotMap.ContainsKey("blend"))
+						slotData.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], false);
+					else
+						slotData.blendMode = BlendMode.normal;
 
 					skeletonData.slots.Add(slotData);
 				}

+ 2 - 2
spine-csharp/src/SlotData.cs

@@ -36,7 +36,7 @@ namespace Spine {
 		internal BoneData boneData;
 		internal float r = 1, g = 1, b = 1, a = 1;
 		internal String attachmentName;
-		internal bool additiveBlending;
+		internal BlendMode blendMode;
 
 		public String Name { get { return name; } }
 		public BoneData BoneData { get { return boneData; } }
@@ -46,7 +46,7 @@ namespace Spine {
 		public float A { get { return a; } set { a = value; } }
 		/// <summary>May be null.</summary>
 		public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } }
-		public bool AdditiveBlending { get { return additiveBlending; } set { additiveBlending = value; } }
+		public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }
 
 		public SlotData (String name, BoneData boneData) {
 			if (name == null) throw new ArgumentNullException("name cannot be null.");

+ 9 - 2
spine-js/spine.js

@@ -50,6 +50,13 @@ spine.BoneData.prototype = {
 	flipX: false, flipY: false
 };
 
+spine.BlendMode = {
+	normal: 0,
+	additive: 1,
+	multiply: 2,
+	screen: 3
+};
+
 spine.SlotData = function (name, boneData) {
 	this.name = name;
 	this.boneData = boneData;
@@ -57,7 +64,7 @@ spine.SlotData = function (name, boneData) {
 spine.SlotData.prototype = {
 	r: 1, g: 1, b: 1, a: 1,
 	attachmentName: null,
-	additiveBlending: false
+	blendMode: spine.BlendMode.normal
 };
 
 spine.IkConstraintData = function (name) {
@@ -1766,7 +1773,7 @@ spine.SkeletonJson.prototype = {
 			}
 
 			slotData.attachmentName = slotMap["attachment"];
-			slotData.additiveBlending = slotMap["additive"] && slotMap["additive"] == "true";
+			slotData.blendMode = spine.AttachmentType[slotMap["blend"] || "normal"];
 
 			skeletonData.slots.push(slotData);
 		}

+ 30 - 0
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BlendMode.java

@@ -0,0 +1,30 @@
+
+package com.esotericsoftware.spine;
+
+import com.badlogic.gdx.graphics.GL20;
+
+public enum BlendMode {
+	normal(GL20.GL_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA), //
+	additive(GL20.GL_SRC_ALPHA, GL20.GL_ONE, GL20.GL_ONE), //
+	multiply(GL20.GL_DST_COLOR, GL20.GL_DST_COLOR, GL20.GL_ONE_MINUS_SRC_ALPHA), //
+	screen(GL20.GL_ONE, GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_COLOR), //
+	;
+
+	int source, sourcePMA, dest;
+
+	BlendMode (int source, int sourcePremultipledAlpha, int dest) {
+		this.source = source;
+		this.sourcePMA = sourcePremultipledAlpha;
+		this.dest = dest;
+	}
+
+	public int getSource (boolean premultipliedAlpha) {
+		return premultipliedAlpha ? sourcePMA : source;
+	}
+
+	public int getDest () {
+		return dest;
+	}
+
+	static public BlendMode[] values = values();
+}

+ 2 - 2
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

@@ -160,7 +160,7 @@ public class SkeletonBinary {
 				SlotData slotData = new SlotData(slotName, boneData);
 				Color.rgba8888ToColor(slotData.color, input.readInt());
 				slotData.attachmentName = input.readString();
-				slotData.additiveBlending = input.readBoolean();
+				slotData.blendMode = BlendMode.values[input.readInt(true)];
 				skeletonData.slots.add(slotData);
 			}
 
@@ -227,7 +227,7 @@ public class SkeletonBinary {
 		String name = input.readString();
 		if (name == null) name = attachmentName;
 
-		switch (AttachmentType.values()[input.readByte()]) {
+		switch (AttachmentType.values[input.readByte()]) {
 		case region: {
 			String path = input.readString();
 			if (path == null) path = name;

+ 1 - 3
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java

@@ -161,9 +161,7 @@ public class SkeletonJson {
 			if (color != null) slotData.getColor().set(Color.valueOf(color));
 
 			slotData.attachmentName = slotMap.getString("attachment", null);
-
-			slotData.additiveBlending = slotMap.getBoolean("additive", false);
-
+			slotData.blendMode = BlendMode.valueOf(slotMap.getString("blend", BlendMode.normal.name()));
 			skeletonData.slots.add(slotData);
 		}
 

+ 14 - 26
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java

@@ -30,18 +30,16 @@
 
 package com.esotericsoftware.spine;
 
+import com.badlogic.gdx.graphics.Texture;
+import com.badlogic.gdx.graphics.g2d.Batch;
+import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
+import com.badlogic.gdx.utils.Array;
 import com.esotericsoftware.spine.attachments.Attachment;
 import com.esotericsoftware.spine.attachments.MeshAttachment;
 import com.esotericsoftware.spine.attachments.RegionAttachment;
 import com.esotericsoftware.spine.attachments.SkeletonAttachment;
 import com.esotericsoftware.spine.attachments.SkinnedMeshAttachment;
 
-import com.badlogic.gdx.graphics.GL20;
-import com.badlogic.gdx.graphics.Texture;
-import com.badlogic.gdx.graphics.g2d.Batch;
-import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
-import com.badlogic.gdx.utils.Array;
-
 public class SkeletonRenderer {
 	static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0};
 
@@ -50,10 +48,7 @@ public class SkeletonRenderer {
 	@SuppressWarnings("null")
 	public void draw (PolygonSpriteBatch batch, Skeleton skeleton) {
 		boolean premultipliedAlpha = this.premultipliedAlpha;
-		int srcFunc = premultipliedAlpha ? GL20.GL_ONE : GL20.GL_SRC_ALPHA;
-		batch.setBlendFunction(srcFunc, GL20.GL_ONE_MINUS_SRC_ALPHA);
-
-		boolean additive = false;
+		BlendMode blendMode = null;
 
 		float[] vertices = null;
 		short[] triangles = null;
@@ -106,12 +101,10 @@ public class SkeletonRenderer {
 			}
 
 			if (texture != null) {
-				if (slot.data.getAdditiveBlending() != additive) {
-					additive = !additive;
-					if (additive)
-						batch.setBlendFunction(srcFunc, GL20.GL_ONE);
-					else
-						batch.setBlendFunction(srcFunc, GL20.GL_ONE_MINUS_SRC_ALPHA);
+				BlendMode slotBlendMode = slot.data.getBlendMode();
+				if (slotBlendMode != blendMode) {
+					blendMode = slotBlendMode;
+					batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
 				}
 				batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length);
 			}
@@ -120,10 +113,7 @@ public class SkeletonRenderer {
 
 	public void draw (Batch batch, Skeleton skeleton) {
 		boolean premultipliedAlpha = this.premultipliedAlpha;
-		int srcFunc = premultipliedAlpha ? GL20.GL_ONE : GL20.GL_SRC_ALPHA;
-		batch.setBlendFunction(srcFunc, GL20.GL_ONE_MINUS_SRC_ALPHA);
-
-		boolean additive = false;
+		BlendMode blendMode = null;
 
 		Array<Slot> drawOrder = skeleton.drawOrder;
 		for (int i = 0, n = drawOrder.size; i < n; i++) {
@@ -133,12 +123,10 @@ public class SkeletonRenderer {
 				RegionAttachment regionAttachment = (RegionAttachment)attachment;
 				regionAttachment.updateWorldVertices(slot, premultipliedAlpha);
 				float[] vertices = regionAttachment.getWorldVertices();
-				if (slot.data.getAdditiveBlending() != additive) {
-					additive = !additive;
-					if (additive)
-						batch.setBlendFunction(srcFunc, GL20.GL_ONE);
-					else
-						batch.setBlendFunction(srcFunc, GL20.GL_ONE_MINUS_SRC_ALPHA);
+				BlendMode slotBlendMode = slot.data.getBlendMode();
+				if (slotBlendMode != blendMode) {
+					blendMode = slotBlendMode;
+					batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
 				}
 				batch.draw(regionAttachment.getRegion().getTexture(), vertices, 0, 20);
 

+ 5 - 5
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java

@@ -37,7 +37,7 @@ public class SlotData {
 	final BoneData boneData;
 	final Color color = new Color(1, 1, 1, 1);
 	String attachmentName;
-	boolean additiveBlending;
+	BlendMode blendMode;
 
 	SlotData () {
 		name = null;
@@ -73,12 +73,12 @@ public class SlotData {
 		return attachmentName;
 	}
 
-	public boolean getAdditiveBlending () {
-		return additiveBlending;
+	public BlendMode getBlendMode () {
+		return blendMode;
 	}
 
-	public void setAdditiveBlending (boolean additiveBlending) {
-		this.additiveBlending = additiveBlending;
+	public void setBlendMode (BlendMode blendMode) {
+		this.blendMode = blendMode;
 	}
 
 	public String toString () {

+ 3 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java

@@ -31,5 +31,7 @@
 package com.esotericsoftware.spine.attachments;
 
 public enum AttachmentType {
-	region, boundingbox, mesh, skinnedmesh
+	region, boundingbox, mesh, skinnedmesh;
+
+	static public AttachmentType[] values = values();
 }

+ 8 - 3
spine-love/spine-love/spine.lua

@@ -52,6 +52,7 @@ spine.AnimationState = require "spine-lua.AnimationState"
 spine.EventData = require "spine-lua.EventData"
 spine.Event = require "spine-lua.Event"
 spine.SkeletonBounds = require "spine-lua.SkeletonBounds"
+spine.BlendMode = require "spine-lua.BlendMode"
 
 spine.utils.readFile = function (fileName, base)
 	local path = fileName
@@ -139,10 +140,14 @@ function spine.Skeleton.new (skeletonData, group)
 					rotation = -rotation
 				end
 				love.graphics.setColor(r * slot.r, g * slot.g, b * slot.b, a * slot.a)
-				if slot.data.additiveBlending then
-					love.graphics.setBlendMode("additive")
-				else
+				if slot.data.blendMode == spine.BlendMode.normal then
 					love.graphics.setBlendMode("alpha")
+				elseif slot.data.blendMode == spine.BlendMode.additive then
+					love.graphics.setBlendMode("additive")
+				elseif slot.data.blendMode == spine.BlendMode.multiply then
+					love.graphics.setBlendMode("multiply")
+				elseif slot.data.blendMode == spine.BlendMode.screen then
+					love.graphics.setBlendMode("screen")
 				end
 				love.graphics.draw(image, 
 					self.x + x, 

+ 37 - 0
spine-lua/BlendMode.lua

@@ -0,0 +1,37 @@
+-------------------------------------------------------------------------------
+-- Spine Runtimes Software License
+-- Version 2.1
+-- 
+-- Copyright (c) 2013, Esoteric Software
+-- All rights reserved.
+-- 
+-- You are granted a perpetual, non-exclusive, non-sublicensable and
+-- non-transferable license to install, execute and perform the Spine Runtimes
+-- Software (the "Software") solely for internal use. Without the written
+-- permission of Esoteric Software (typically granted by licensing Spine), you
+-- may not (a) modify, translate, adapt or otherwise create derivative works,
+-- improvements of the Software or develop new applications using the Software
+-- or (b) remove, delete, alter or obscure any trademarks or any copyright,
+-- trademark, patent or other intellectual property or proprietary rights
+-- notices on or in the Software, including any copy thereof. Redistributions
+-- in binary or source form must include this license and terms.
+-- 
+-- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
+-- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+-- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+-- EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+-- OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+-- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+-- OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-------------------------------------------------------------------------------
+
+local BlendMode = {
+	normal = 0,
+	additive = 1,
+	multiply = 2,
+	screen = 3
+}
+return BlendMode

+ 2 - 1
spine-lua/SkeletonJson.lua

@@ -39,6 +39,7 @@ local IkConstraint = require "spine-lua.IkConstraint"
 local EventData = require "spine-lua.EventData"
 local Event = require "spine-lua.Event"
 local AttachmentType = require "spine-lua.AttachmentType"
+local BlendMode = require "spine-lua.BlendMode"
 
 local SkeletonJson = {}
 function SkeletonJson.new (attachmentLoader)
@@ -154,7 +155,7 @@ function SkeletonJson.new (attachmentLoader)
 				end
 
 				slotData.attachmentName = slotMap["attachment"]
-				slotData.additiveBlending = slotMap["additive"]
+				slotData.blendMode = BlendMode[slotMap["blend"] or "normal"]
 
 				table.insert(skeletonData.slots, slotData)
 				skeletonData.slotNameIndices[slotData.name] = #skeletonData.slots

+ 3 - 1
spine-lua/SlotData.lua

@@ -28,6 +28,8 @@
 -- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 -------------------------------------------------------------------------------
 
+local BlendMode = require "spine-lua.BlendMode"
+
 local SlotData = {}
 function SlotData.new (name, boneData)
 	if not name then error("name cannot be nil", 2) end
@@ -38,7 +40,7 @@ function SlotData.new (name, boneData)
 		boneData = boneData,
 		r = 1, g = 1, b = 1, a = 1,
 		attachmentName = nil,
-		additiveBlending = false
+		blendMode = BlendMode.normal
 	}
 
 	function self:setColor (r, g, b, a)

+ 19 - 8
spine-sfml/src/spine/spine-sfml.cpp

@@ -95,7 +95,6 @@ void SkeletonDrawable::update (float deltaTime) {
 
 void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
 	vertexArray->clear();
-	states.blendMode = BlendAlpha;
 
 	sf::Vertex vertices[4];
 	sf::Vertex vertex;
@@ -103,6 +102,25 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
 		Slot* slot = skeleton->drawOrder[i];
 		Attachment* attachment = slot->attachment;
 		if (!attachment) continue;
+
+		sf::BlendMode blend;
+		switch (slot->data->blendMode) {
+		case BLEND_MODE_ADDITIVE:
+			blend = BlendAdd;
+			break;
+		case BLEND_MODE_MULTIPLY:
+			blend = BlendMultiply;
+			break;
+		case BLEND_MODE_SCREEN: // Unsupported, fall through.
+		default:
+			blend = BlendAlpha;
+		}
+		if (states.blendMode != blend) {
+			target.draw(*vertexArray, states);
+			vertexArray->clear();
+			states.blendMode = blend;
+		}
+
 		Texture* texture = 0;
 		if (attachment->type == ATTACHMENT_REGION) {
 			RegionAttachment* regionAttachment = (RegionAttachment*)attachment;
@@ -212,13 +230,6 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
 		if (texture) {
 			// SMFL doesn't handle batching for us, so we'll just force a single texture per skeleton.
 			states.texture = texture;
-
-			BlendMode blend = slot->data->additiveBlending ? BlendAdd : BlendAlpha;
-			if (states.blendMode != blend) {
-				target.draw(*vertexArray, states);
-				vertexArray->clear();
-				states.blendMode = blend;
-			}
 		}
 	}
 	target.draw(*vertexArray, states);

+ 14 - 8
spine-starling/spine-starling/src/spine/starling/PolygonBatch.as

@@ -41,6 +41,9 @@ import flash.geom.Matrix;
 import flash.geom.Point;
 import flash.utils.Dictionary;
 
+import spine.BlendMode;
+import spine.flash.SkeletonSprite;
+
 import starling.core.RenderSupport;
 import starling.core.Starling;
 import starling.display.BlendMode;
@@ -61,8 +64,8 @@ internal class PolygonBatch {
 	private var _texture:Texture;
 	private var _support:RenderSupport;
 	private var _programBits:uint;
-	private var _blendMode:String;
-	private var _additive:Boolean;
+	private var _blendModeNormal:String;
+	private var _blendMode:spine.BlendMode;
 	private var _alpha:Number;
 
 	private var _verticesCount:int;
@@ -88,11 +91,11 @@ internal class PolygonBatch {
 		_support = support;
 		_alpha = alpha;
 		_programBits = 0xffffffff;
-		_additive = false;
+		_blendMode = null;
 
 		support.finishQuadBatch();
 		support.blendMode = blendMode;
-		_blendMode = support.blendMode;
+		_blendModeNormal = support.blendMode;
 
 		var context:Context3D = Starling.context;
 		context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 1, support.mvpMatrix3D, true);
@@ -115,11 +118,14 @@ internal class PolygonBatch {
 	}
 
 	public function add (texture:Texture, vertices:Vector.<Number>, vl:int, uvs:Vector.<Number>, triangles:Vector.<uint>,
-						 r:Number, g:Number, b:Number, a:Number, additive:Boolean, matrix:Matrix) : void {
-		if (additive != _additive) {
-			_additive = additive;
+						 r:Number, g:Number, b:Number, a:Number, blendMode:spine.BlendMode, matrix:Matrix) : void {
+		if (blendMode != _blendMode) {
+			_blendMode = blendMode;
 			flush();
-			_support.blendMode = additive ? BlendMode.ADD : _blendMode;
+			if (blendMode == spine.BlendMode.normal)
+				_support.blendMode = _blendModeNormal;
+			else
+				_support.blendMode = spine.starling.SkeletonSprite.blendModes[blendMode.ordinal];
 			_support.applyBlendMode(true);
 		}
 

+ 4 - 2
spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as

@@ -58,6 +58,8 @@ public class SkeletonSprite extends DisplayObject {
 	static private var _tempMatrix:Matrix = new Matrix();
 	static private var _tempVertices:Vector.<Number> = new Vector.<Number>(8);
 	static private var _quadTriangles:Vector.<uint> = new <uint>[0, 1, 2, 2, 3, 0];
+	static internal var blendModes:Vector.<String> = new <String>[
+		BlendMode.NORMAL, BlendMode.ADD, BlendMode.MULTIPLY, BlendMode.SCREEN];
 
 	private var _skeleton:Skeleton;
 	private var _polygonBatch:PolygonBatch;
@@ -165,7 +167,7 @@ public class SkeletonSprite extends DisplayObject {
 				r *= skeletonR * slot.r * a;
 				g *= skeletonG * slot.g * a;
 				b *= skeletonB * slot.b * a;
-				polygonBatch.add(image.texture, worldVertices, verticesLength, uvs, triangles, r, g, b, a, slot.data.additiveBlending, matrix);
+				polygonBatch.add(image.texture, worldVertices, verticesLength, uvs, triangles, r, g, b, a, slot.data.blendMode, matrix);
 			}
 		}
 	}
@@ -206,7 +208,7 @@ public class SkeletonSprite extends DisplayObject {
 				vertexData.setColorAndAlpha(3, rgb, a);
 				
 				image.updateVertices();
-				support.blendMode = slot.data.additiveBlending ? BlendMode.ADD : blendMode;
+				support.blendMode = blendModes[slot.data.blendMode.ordinal];
 				support.batchQuad(image, alpha, image.texture, _smoothing);
 			}
 		}

+ 1 - 1
spine-turbulenz/example/index.html

@@ -191,7 +191,7 @@ function drawSkeleton (batch, skeleton) {
 		if (!(attachment instanceof spine.RegionAttachment)) continue;
 		attachment.computeVertices(skeleton.x, skeleton.y, slot.bone, vertices);
 		
-		var blendMode = slot.data.additiveBlending ? draw2D.blend.additive : draw2D.blend.alpha;
+		var blendMode = slot.data.blendMode == spine.BlendMode.additive ? draw2D.blend.additive : draw2D.blend.alpha;
 		if (batch.blendMode != blendMode) {
 			batch.end();
 			batch.begin(blendMode);

+ 3 - 6
spine-unity/Assets/spine-unity/SkeletonRenderer.cs

@@ -283,8 +283,7 @@ public class SkeletonRenderer : MonoBehaviour {
 				color.r = (byte)(r * slot.r * regionAttachment.r * color.a);
 				color.g = (byte)(g * slot.g * regionAttachment.g * color.a);
 				color.b = (byte)(b * slot.b * regionAttachment.b * color.a);
-				if (slot.data.additiveBlending)
-					color.a = 0;
+				if (slot.data.blendMode == BlendMode.additive) color.a = 0;
 				colors[vertexIndex] = color;
 				colors[vertexIndex + 1] = color;
 				colors[vertexIndex + 2] = color;
@@ -311,8 +310,7 @@ public class SkeletonRenderer : MonoBehaviour {
 					color.r = (byte)(r * slot.r * meshAttachment.r * color.a);
 					color.g = (byte)(g * slot.g * meshAttachment.g * color.a);
 					color.b = (byte)(b * slot.b * meshAttachment.b * color.a);
-					if (slot.data.additiveBlending)
-						color.a = 0;
+					if (slot.data.blendMode == BlendMode.additive) color.a = 0;
 
 					float[] meshUVs = meshAttachment.uvs;
 					float z = i * zSpacing;
@@ -332,8 +330,7 @@ public class SkeletonRenderer : MonoBehaviour {
 					color.r = (byte)(r * slot.r * meshAttachment.r * color.a);
 					color.g = (byte)(g * slot.g * meshAttachment.g * color.a);
 					color.b = (byte)(b * slot.b * meshAttachment.b * color.a);
-					if (slot.data.additiveBlending)
-						color.a = 0;
+					if (slot.data.blendMode == BlendMode.additive) color.a = 0;
 
 					float[] meshUVs = meshAttachment.uvs;
 					float z = i * zSpacing;

+ 1 - 1
spine-xna/src/SkeletonMeshRenderer.cs

@@ -96,7 +96,7 @@ namespace Spine {
 				Attachment attachment = slot.Attachment;
 				if (attachment is RegionAttachment) {
 					RegionAttachment regionAttachment = (RegionAttachment)attachment;
-					BlendState blend = slot.Data.AdditiveBlending ? BlendState.Additive : defaultBlendState;
+					BlendState blend = slot.Data.BlendMode == BlendMode.additive ? BlendState.Additive : defaultBlendState;
 					if (device.BlendState != blend) {
 						End();
 						device.BlendState = blend;

+ 1 - 1
spine-xna/src/SkeletonRegionRenderer.cs

@@ -88,7 +88,7 @@ namespace Spine {
 				Slot slot = drawOrder[i];
 				RegionAttachment regionAttachment = slot.Attachment as RegionAttachment;
 				if (regionAttachment != null) {
-					BlendState blend = slot.Data.AdditiveBlending ? BlendState.Additive : defaultBlendState;
+					BlendState blend = slot.Data.BlendMode == BlendMode.additive ? BlendState.Additive : defaultBlendState;
 					if (device.BlendState != blend) {
 						End();
 						device.BlendState = blend;