From f8a76c645586f0a487efb64748ab587de4b48edc Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Thu, 2 Apr 2015 14:20:20 +0200 Subject: [PATCH] Blend modes for all runtimes. --- spine-as3/spine-as3-example/src/spineboy.json | 2 +- spine-as3/spine-as3/src/spine/BlendMode.as | 46 +++++++++++ spine-as3/spine-as3/src/spine/SkeletonJson.as | 2 +- spine-as3/spine-as3/src/spine/SlotData.as | 2 +- .../src/spine/flash/SkeletonSprite.as | 4 +- spine-c/include/spine/Slot.h | 2 +- spine-c/include/spine/SlotData.h | 13 +++- spine-c/src/spine/SkeletonJson.c | 16 +++- .../2/src/spine/SkeletonRenderer.m | 20 ++++- .../3/src/spine/SkeletonRenderer.h | 5 +- .../3/src/spine/SkeletonRenderer.m | 76 ++++++++++++------- .../2/src/spine/SkeletonRenderer.cpp | 20 ++++- .../3/src/spine/SkeletonRenderer.cpp | 20 ++++- spine-corona/spine-corona/spine.lua | 11 ++- spine-csharp/spine-csharp.csproj | 1 + spine-csharp/spine-csharp_xna.csproj | 1 + spine-csharp/src/BlendMode.cs | 35 +++++++++ spine-csharp/src/SkeletonBinary.cs | 2 +- spine-csharp/src/SkeletonJson.cs | 6 +- spine-csharp/src/SlotData.cs | 4 +- spine-js/spine.js | 11 ++- .../com/esotericsoftware/spine/BlendMode.java | 30 ++++++++ .../spine/SkeletonBinary.java | 4 +- .../esotericsoftware/spine/SkeletonJson.java | 4 +- .../spine/SkeletonRenderer.java | 40 ++++------ .../com/esotericsoftware/spine/SlotData.java | 10 +-- .../spine/attachments/AttachmentType.java | 4 +- spine-love/spine-love/spine.lua | 11 ++- spine-lua/BlendMode.lua | 37 +++++++++ spine-lua/SkeletonJson.lua | 3 +- spine-lua/SlotData.lua | 4 +- spine-sfml/src/spine/spine-sfml.cpp | 27 +++++-- .../src/spine/starling/PolygonBatch.as | 22 ++++-- .../src/spine/starling/SkeletonSprite.as | 6 +- spine-turbulenz/example/index.html | 2 +- .../Assets/spine-unity/SkeletonRenderer.cs | 9 +-- spine-xna/src/SkeletonMeshRenderer.cs | 2 +- spine-xna/src/SkeletonRegionRenderer.cs | 2 +- 38 files changed, 385 insertions(+), 131 deletions(-) create mode 100644 spine-as3/spine-as3/src/spine/BlendMode.as create mode 100644 spine-csharp/src/BlendMode.cs create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BlendMode.java create mode 100644 spine-lua/BlendMode.lua diff --git a/spine-as3/spine-as3-example/src/spineboy.json b/spine-as3/spine-as3-example/src/spineboy.json index 7b4497712..1bd1b64c1 100644 --- a/spine-as3/spine-as3-example/src/spineboy.json +++ b/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": { diff --git a/spine-as3/spine-as3/src/spine/BlendMode.as b/spine-as3/spine-as3/src/spine/BlendMode.as new file mode 100644 index 000000000..54cc5f0c6 --- /dev/null +++ b/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; + } +} + +} diff --git a/spine-as3/spine-as3/src/spine/SkeletonJson.as b/spine-as3/spine-as3/src/spine/SkeletonJson.as index 9bceb7bf9..702f5b29c 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonJson.as +++ b/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; } diff --git a/spine-as3/spine-as3/src/spine/SlotData.as b/spine-as3/spine-as3/src/spine/SlotData.as index 2cba62223..2561c4597 100644 --- a/spine-as3/spine-as3/src/spine/SlotData.as +++ b/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."); diff --git a/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as b/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as index 656892827..dc9ba6a10 100644 --- a/spine-as3/spine-as3/src/spine/flash/SkeletonSprite.as +++ b/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. = new [ + 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; diff --git a/spine-c/include/spine/Slot.h b/spine-c/include/spine/Slot.h index 21ac76061..93cf57052 100644 --- a/spine-c/include/spine/Slot.h +++ b/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), diff --git a/spine-c/include/spine/SlotData.h b/spine-c/include/spine/SlotData.h index b1e00faf2..e2409b9c9 100644 --- a/spine-c/include/spine/SlotData.h +++ b/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__) diff --git a/spine-c/src/spine/SkeletonJson.c b/spine-c/src/spine/SkeletonJson.c index 8e2b1280e..0411476cc 100644 --- a/spine-c/src/spine/SkeletonJson.c +++ b/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); + item = Json_getItem(slotMap, "attachment"); + if (item) spSlotData_setAttachmentName(slotData, item->valueString); - slotData->additiveBlending = Json_getInt(slotMap, "additive", 0); + 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; } diff --git a/spine-cocos2d-iphone/2/src/spine/SkeletonRenderer.m b/spine-cocos2d-iphone/2/src/spine/SkeletonRenderer.m index ef0d5d0d8..655df0dd5 100644 --- a/spine-cocos2d-iphone/2/src/spine/SkeletonRenderer.m +++ b/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; diff --git a/spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.h b/spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.h index ec91328af..69a709f68 100644 --- a/spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.h +++ b/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; diff --git a/spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.m b/spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.m index bd57ebec2..42f7b1a49 100644 --- a/spine-cocos2d-iphone/3/src/spine/SkeletonRenderer.m +++ b/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); diff --git a/spine-cocos2dx/2/src/spine/SkeletonRenderer.cpp b/spine-cocos2dx/2/src/spine/SkeletonRenderer.cpp index 0a2fec9e9..f82edda89 100644 --- a/spine-cocos2dx/2/src/spine/SkeletonRenderer.cpp +++ b/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; diff --git a/spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp b/spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp index 3b48f2b7d..c4640efa5 100644 --- a/spine-cocos2dx/3/src/spine/SkeletonRenderer.cpp +++ b/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; diff --git a/spine-corona/spine-corona/spine.lua b/spine-corona/spine-corona/spine.lua index 9231f6f50..46aecb8d4 100644 --- a/spine-corona/spine-corona/spine.lua +++ b/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. diff --git a/spine-csharp/spine-csharp.csproj b/spine-csharp/spine-csharp.csproj index c094972ca..7cc986591 100644 --- a/spine-csharp/spine-csharp.csproj +++ b/spine-csharp/spine-csharp.csproj @@ -64,6 +64,7 @@ + diff --git a/spine-csharp/spine-csharp_xna.csproj b/spine-csharp/spine-csharp_xna.csproj index 9566f3d61..df66b3e9e 100644 --- a/spine-csharp/spine-csharp_xna.csproj +++ b/spine-csharp/spine-csharp_xna.csproj @@ -103,6 +103,7 @@ + diff --git a/spine-csharp/src/BlendMode.cs b/spine-csharp/src/BlendMode.cs new file mode 100644 index 000000000..7e6942bf6 --- /dev/null +++ b/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 + } +} diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index 02e4aea2e..250ba0486 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/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); } diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 4bebe56f5..f9af99d4c 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/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); } diff --git a/spine-csharp/src/SlotData.cs b/spine-csharp/src/SlotData.cs index dd74f4ff1..75630bed4 100644 --- a/spine-csharp/src/SlotData.cs +++ b/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; } } /// May be null. 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."); diff --git a/spine-js/spine.js b/spine-js/spine.js index 9d5ade2a4..c12932745 100644 --- a/spine-js/spine.js +++ b/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); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BlendMode.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BlendMode.java new file mode 100644 index 000000000..3444e7bbe --- /dev/null +++ b/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(); +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index 1c32ccfa1..cd0c50ab3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/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; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index 9147377b1..3e2db1047 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/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); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java index e0c58a241..79e264182 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/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 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); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java index d434ff09c..7e43ee6f3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java +++ b/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 () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java index ba75c605b..0bb82c340 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java +++ b/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(); } diff --git a/spine-love/spine-love/spine.lua b/spine-love/spine-love/spine.lua index 894355d0e..630ff1691 100644 --- a/spine-love/spine-love/spine.lua +++ b/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, diff --git a/spine-lua/BlendMode.lua b/spine-lua/BlendMode.lua new file mode 100644 index 000000000..5e3d3e972 --- /dev/null +++ b/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 diff --git a/spine-lua/SkeletonJson.lua b/spine-lua/SkeletonJson.lua index c1dd265f4..3c7f01b22 100755 --- a/spine-lua/SkeletonJson.lua +++ b/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 diff --git a/spine-lua/SlotData.lua b/spine-lua/SlotData.lua index 70db649de..82c7b3a06 100644 --- a/spine-lua/SlotData.lua +++ b/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) diff --git a/spine-sfml/src/spine/spine-sfml.cpp b/spine-sfml/src/spine/spine-sfml.cpp index b5b5b06de..c0d1bf425 100644 --- a/spine-sfml/src/spine/spine-sfml.cpp +++ b/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); diff --git a/spine-starling/spine-starling/src/spine/starling/PolygonBatch.as b/spine-starling/spine-starling/src/spine/starling/PolygonBatch.as index 9085f03b9..d026dd48d 100644 --- a/spine-starling/spine-starling/src/spine/starling/PolygonBatch.as +++ b/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., vl:int, uvs:Vector., triangles:Vector., - 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); } diff --git a/spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as b/spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as index 9ac1e58fc..a7c3f5ab5 100644 --- a/spine-starling/spine-starling/src/spine/starling/SkeletonSprite.as +++ b/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. = new Vector.(8); static private var _quadTriangles:Vector. = new [0, 1, 2, 2, 3, 0]; + static internal var blendModes:Vector. = new [ + 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); } } diff --git a/spine-turbulenz/example/index.html b/spine-turbulenz/example/index.html index 66dd38414..a8b44eb7f 100644 --- a/spine-turbulenz/example/index.html +++ b/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); diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index b87c9ffcd..6ea9ba231 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/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; diff --git a/spine-xna/src/SkeletonMeshRenderer.cs b/spine-xna/src/SkeletonMeshRenderer.cs index 48697ba4e..9a2bbc177 100644 --- a/spine-xna/src/SkeletonMeshRenderer.cs +++ b/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; diff --git a/spine-xna/src/SkeletonRegionRenderer.cs b/spine-xna/src/SkeletonRegionRenderer.cs index d9d7448ec..1f18ea303 100644 --- a/spine-xna/src/SkeletonRegionRenderer.cs +++ b/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;