[starling] Added two color tinting. Closes part of #843

This commit is contained in:
badlogic 2017-03-10 11:28:51 +01:00
parent 711c731b0f
commit 4935284449
17 changed files with 316 additions and 84 deletions

View File

@ -27,6 +27,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.examples {
import flash.display.Sprite;

View File

@ -1,4 +1,5 @@
eclipse.preferences.version=1
encoding//src/spine/SkeletonJson.as=UTF-8
encoding//src/spine/animation/TwoColorTimeline.as=UTF-8
encoding//src/spine/attachments/PointAttachment.as=UTF-8
encoding/<project>=UTF-8

View File

@ -134,7 +134,7 @@ package spine {
var dark : String = slotMap["dark"];
if (dark) {
slotData.darkColor.setFrom(toColor(dark, 0), toColor(dark, 1), toColor(dark, 2), toColor(dark, 3));
slotData.darkColor = new Color(toColor(dark, 0), toColor(dark, 1), toColor(dark, 2), 0);
}
slotData.attachmentName = slotMap["attachment"];
@ -438,7 +438,7 @@ package spine {
frameIndex = 0;
for each (valueMap in values) {
color = valueMap["color"];
color = valueMap["light"];
var darkColor : String = valueMap["dark"];
var light : Color = new Color(0, 0, 0, 0);
var dark : Color = new Color(0, 0, 0, 0);
@ -710,8 +710,8 @@ package spine {
}
static private function toColor(hexString : String, colorIndex : int) : Number {
if (hexString.length != 8) throw new ArgumentError("Color hexidecimal length must be 8, received: " + hexString);
return parseInt(hexString.substring(colorIndex * 2, colorIndex * 2 + 2), 16) / 255;
if (hexString.length != 8 && hexString.length != 6) throw new ArgumentError("Color hexidecimal length must be 6 or 8, received: " + hexString);
return parseInt(hexString.substring(colorIndex * 2, colorIndex * 2 + 2), 16) / 255;
}
static private function getFloatArray(map : Object, name : String, scale : Number) : Vector.<Number> {

View File

@ -103,9 +103,9 @@ package spine.animation {
g += (frames[frame + G] - g) * percent;
b += (frames[frame + B] - b) * percent;
a += (frames[frame + A] - a) * percent;
r2 += (frames[frame + R2] - a) * percent;
g2 += (frames[frame + G2] - a) * percent;
b2 += (frames[frame + B2] - a) * percent;
r2 += (frames[frame + R2] - r2) * percent;
g2 += (frames[frame + G2] - g2) * percent;
b2 += (frames[frame + B2] - b2) * percent;
}
if (alpha == 1) {
slot.color.setFrom(r, g, b, a);

View File

@ -1,3 +1,4 @@
eclipse.preferences.version=1
encoding//src/spine/examples/TankExample.as=UTF-8
encoding//src/spine/examples/TwoColorExample.as=UTF-8
encoding/<project>=UTF-8

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -39,12 +39,13 @@ package spine.examples {
public function Main() {
var example : Class;
// example = SpineboyExample;
example = SpineboyExample;
// example = GoblinsExample;
// example = RaptorExample;
// example = TankExample;
// example = VineExample;
example = StretchymanExample;
// example = StretchymanExample;
example = TwoColorExample;
_starling = new Starling(example, stage);
_starling.enableErrorChecking = true;

View File

@ -0,0 +1,71 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable, and
* non-transferable license to use, install, execute, and perform the Spine
* Runtimes software and derivative works solely for personal or internal
* use. Without the written permission of Esoteric Software (see Section 2 of
* the Spine Software License Agreement), you may not (a) modify, translate,
* adapt, or develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes or (b) remove,
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
* or other intellectual property or proprietary rights notices on or in the
* Software, including any copy thereof. Redistributions in binary or source
* form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package spine.examples {
import spine.*;
import spine.atlas.Atlas;
import spine.attachments.AtlasAttachmentLoader;
import spine.attachments.AttachmentLoader;
import spine.starling.SkeletonAnimation;
import spine.starling.StarlingTextureLoader;
import starling.core.Starling;
import starling.display.Sprite;
public class TwoColorExample extends Sprite {
[Embed(source = "/TwoColorTest.json", mimeType = "application/octet-stream")]
static public const VineJson : Class;
[Embed(source = "/TwoColorTest.atlas", mimeType = "application/octet-stream")]
static public const VineAtlas : Class;
[Embed(source = "/TwoColorTest.png")]
static public const VineAtlasTexture : Class;
private var skeleton : SkeletonAnimation;
public function TwoColorExample() {
var attachmentLoader : AttachmentLoader;
var spineAtlas : Atlas = new Atlas(new VineAtlas(), new StarlingTextureLoader(new VineAtlasTexture()));
attachmentLoader = new AtlasAttachmentLoader(spineAtlas);
var json : SkeletonJson = new SkeletonJson(attachmentLoader);
json.scale = 0.5;
var skeletonData : SkeletonData = json.readSkeletonData(new VineJson());
skeleton = new SkeletonAnimation(skeletonData);
skeleton.x = 300;
skeleton.y = 200;
skeleton.state.setAnimationByName(0, "animation", true);
addChild(skeleton);
Starling.juggler.add(skeleton);
}
}
}

View File

@ -4,4 +4,6 @@ encoding//src/spine/starling/SkeletonMesh.as=UTF-8
encoding//src/spine/starling/SkeletonSprite.as=UTF-8
encoding//src/spine/starling/StarlingAtlasAttachmentLoader.as=UTF-8
encoding//src/spine/starling/StarlingTextureLoader.as=UTF-8
encoding//src/spine/starling/TwoColorEffect.as=UTF-8
encoding//src/spine/starling/TwoColorMeshStyle.as=UTF-8
encoding/<project>=UTF-8

View File

@ -79,6 +79,7 @@ package spine.starling {
var drawOrder : Vector.<Slot> = skeleton.drawOrder;
var worldVertices : Vector.<Number> = _tempVertices;
var ii : int, iii : int;
var attachmentColor: spine.Color;
var rgb : uint, a : Number;
var dark : uint;
var mesh : SkeletonMesh;
@ -90,59 +91,43 @@ package spine.starling {
var slot : Slot = drawOrder[i];
if (slot.attachment is RegionAttachment) {
var region : RegionAttachment = slot.attachment as RegionAttachment;
region.computeWorldVertices(slot.bone, worldVertices, 0, 2);
a = slot.color.a * region.color.a;
rgb = Color.rgb(r * slot.color.r * region.color.r, g * slot.color.g * region.color.g, b * slot.color.b * region.color.b);
verticesLength = 4 * 2;
verticesCount = verticesLength >> 1;
if (worldVertices.length < verticesLength) worldVertices.length = verticesLength;
region.computeWorldVertices(slot.bone, worldVertices, 0, 2);
var image : Image = region.rendererObject as Image;
if (image == null) {
var origImage : Image = Image(AtlasRegion(region.rendererObject).rendererObject);
region.rendererObject = image = new Image(origImage.texture);
image.style = _twoColorStyle;
for (var j : int = 0; j < 4; j++) {
var p : Point = origImage.getTexCoords(j);
image.setTexCoords(j, p.x, p.y);
}
mesh = region.rendererObject as SkeletonMesh;
if (mesh == null) {
if (region.rendererObject is Image)
region.rendererObject = mesh = new SkeletonMesh(Image(region.rendererObject).texture);
if (region.rendererObject is AtlasRegion)
region.rendererObject = mesh = new SkeletonMesh(Image(AtlasRegion(region.rendererObject).rendererObject).texture);
indexData = mesh.getIndexData();
indices = new <uint>[0, 1, 2, 2, 3, 0];
for (ii = 0; ii < indices.length; ii++)
indexData.setIndex(ii, indices[ii]);
indexData.numIndices = 6;
indexData.trim();
}
if (slot.darkColor == null) dark = Color.rgb(0, 0, 0);
else dark = Color.rgb(slot.darkColor.r * 255, slot.darkColor.g * 255, slot.darkColor.b * 255);
image.setVertexPosition(0, worldVertices[2], worldVertices[3]);
image.setVertexColor(0, rgb);
image.setVertexAlpha(0, a);
image.setVertexPosition(1, worldVertices[4], worldVertices[5]);
image.setVertexColor(1, rgb);
image.setVertexAlpha(1, a);
image.setVertexPosition(2, worldVertices[0], worldVertices[1]);
image.setVertexColor(2, rgb);
image.setVertexAlpha(2, a);
image.setVertexPosition(3, worldVertices[6], worldVertices[7]);
image.setVertexColor(3, rgb);
image.setVertexAlpha(3, a);
image.setRequiresRedraw();
painter.state.blendMode = blendModes[slot.data.blendMode.ordinal];
// FIXME set smoothing/filter
painter.batchMesh(image);
attachmentColor = region.color;
uvs = region.uvs;
} else if (slot.attachment is MeshAttachment) {
var meshAttachment : MeshAttachment = MeshAttachment(slot.attachment);
verticesLength = meshAttachment.worldVerticesLength;
verticesCount = verticesLength >> 1;
if (worldVertices.length < verticesLength) worldVertices.length = verticesLength;
meshAttachment.computeWorldVertices(slot, 0, meshAttachment.worldVerticesLength, worldVertices, 0, 2);
mesh = meshAttachment.rendererObject as SkeletonMesh;
if (mesh == null) {
if (meshAttachment.rendererObject is Image)
meshAttachment.rendererObject = mesh = new SkeletonMesh(Image(meshAttachment.rendererObject).texture);
if (meshAttachment.rendererObject is AtlasRegion)
meshAttachment.rendererObject = mesh = new SkeletonMesh(Image(AtlasRegion(meshAttachment.rendererObject).rendererObject).texture);
}
if (mesh.numIndices != meshAttachment.triangles.length) {
meshAttachment.rendererObject = mesh = new SkeletonMesh(Image(AtlasRegion(meshAttachment.rendererObject).rendererObject).texture);
mesh.setStyle(_twoColorStyle);
indexData = mesh.getIndexData();
indices = meshAttachment.triangles;
indicesLength = meshAttachment.triangles.length;
@ -151,39 +136,34 @@ package spine.starling {
}
indexData.numIndices = indicesLength;
indexData.trim();
}
// FIXME pre-multiplied alpha?
a = slot.color.a * meshAttachment.color.a;
rgb = Color.rgb(r * slot.color.r * meshAttachment.color.r, g * slot.color.g * meshAttachment.color.g, b * slot.color.b * meshAttachment.color.b);
if (slot.darkColor == null) dark = Color.rgb(0, 0, 0);
else dark = Color.rgb(slot.darkColor.r * 255, slot.darkColor.g * 255, slot.darkColor.b * 255);
if (mesh.style.vertexFormat != _twoColorStyle.vertexFormat)
mesh.style = _twoColorStyle;
vertexData = mesh.getVertexData();
uvs = meshAttachment.uvs;
vertexData.colorize("color", rgb, a);
vertexData.colorize("color2", dark);
for (ii = 0, iii = 0; ii < verticesCount; ii++, iii += 2) {
mesh.setVertexPosition(ii, worldVertices[iii], worldVertices[iii + 1]);
mesh.setTexCoords(ii, uvs[iii], uvs[iii + 1]);
}
vertexData.numVertices = verticesCount;
painter.state.blendMode = blendModes[slot.data.blendMode.ordinal];
// FIXME set smoothing/filter
painter.batchMesh(mesh);
}
attachmentColor = meshAttachment.color;
uvs = meshAttachment.uvs;
}
a = slot.color.a * attachmentColor.a;
rgb = Color.rgb(r * slot.color.r * attachmentColor.r, g * slot.color.g * attachmentColor.g, b * slot.color.b * attachmentColor.b);
if (slot.darkColor == null) dark = Color.rgb(0, 0, 0);
else dark = Color.rgb(slot.darkColor.r * 255, slot.darkColor.g * 255, slot.darkColor.b * 255);
// Mesh doesn't retain the style, can't find the reason why
mesh.setStyle(_twoColorStyle);
vertexData = mesh.getVertexData();
vertexData.colorize("color", rgb, a);
vertexData.colorize("color2", dark);
for (ii = 0, iii = 0; ii < verticesCount; ii++, iii += 2) {
mesh.setVertexPosition(ii, worldVertices[iii], worldVertices[iii + 1]);
mesh.setTexCoords(ii, uvs[iii], uvs[iii + 1]);
}
vertexData.numVertices = verticesCount;
painter.state.blendMode = blendModes[slot.data.blendMode.ordinal];
painter.batchMesh(mesh);
}
painter.state.blendMode = originalBlendMode;
}
override public function hitTest(localPoint : Point) : DisplayObject {
// FIXME what to do here?
// if (forTouch && (!visible || !touchable))
// return null;
var minX : Number = Number.MAX_VALUE, minY : Number = Number.MAX_VALUE;
var maxX : Number = -Number.MAX_VALUE, maxY : Number = -Number.MAX_VALUE;
var slots : Vector.<Slot> = skeleton.slots;

View File

@ -29,6 +29,7 @@
*****************************************************************************/
package spine.starling {
import flash.display3D.Context3DProgramType;
import starling.rendering.Program;
import flash.display3D.Context3D;
@ -40,20 +41,28 @@ package spine.starling {
public static const VERTEX_FORMAT : VertexDataFormat = TwoColorMeshStyle.VERTEX_FORMAT;
override protected function createProgram() : Program {
var vertexShader : String = ["m44 op, va0, vc0", // 4x4 matrix transform to output clip-space
// v0 -> tex coords
// v1 -> color plus alpha
// v2 -> dark color
var vertexShader : String = [
"m44 op, va0, vc0", // 4x4 matrix transform to output clip-space
"mov v0, va1 ", // pass texture coordinates to fragment program
"mul v1, va2, vc4", // multiply alpha (vc4) with color (va2), pass to fp
"mov v2, va3 " // pass offset to fp
"mov v2, va3 " // pass dark color to fp
].join("\n");
var fragmentShader : String = [tex("ft0", "v0", 0, texture) + // get color from texture
"mul ft0, ft0, v1", // multiply color with texel color
"mov ft1, v2", // copy complete offset to ft1
"mul ft1.xyz, v2.xyz, ft0.www", // multiply offset.rgb with alpha (pma!)
"add oc, ft0, ft1" // add offset, copy to output
var fragmentShader : String = [
tex("ft0", "v0", 0, texture), // ft0 = texture2d(texCoords)
"mul ft1, ft0, v1", // ft1 = texColor * light
"sub ft2.xyz, fc0.xyz, ft0.xyz", // ft2.xyz = (1 - texColor.rgb)
"mul ft2.xyz, ft2.xyz, v2.xyz", // ft2.xyz = (1 - texColor.rgb) * dark.rgb
"mul ft2.xyz, ft2.xyz, ft1.www", // ft2.xyz = (1 - texColor.rgb) * dark.rgb * alpha
"add ft2.xyz, ft2.xyz, ft1.xyz", // ft2.xyz = (1 - texColor.rgb) * dark.rgb * alpha + texColor.rgb * light.rgb
"mov ft2.w, ft1.w", // ft2.w = alpha
"mov oc, ft2"
].join("\n");
return Program.fromSource(vertexShader, fragmentShader);
return Program.fromSource(vertexShader, fragmentShader);
}
override public function get vertexFormat() : VertexDataFormat {
@ -63,6 +72,9 @@ package spine.starling {
override protected function beforeDraw(context : Context3D) : void {
super.beforeDraw(context);
vertexFormat.setVertexBufferAt(3, vertexBuffer, "color2");
// fc0 -> (1, 1, 1, 1)
context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([1, 1, 1, 1]));
}
override protected function afterDraw(context : Context3D) : void {