Refactored attachments to fill an array rather than store vertices for rendering.

This commit is contained in:
NathanSweet 2016-11-27 02:19:50 +01:00
parent 66b5f49f2e
commit 938db42016
11 changed files with 184 additions and 313 deletions

View File

@ -211,14 +211,14 @@ public class PathConstraint implements Constraint {
} else if (p < 0) {
if (prevCurve != BEFORE) {
prevCurve = BEFORE;
path.computeWorldVertices(target, 2, 4, world, 0);
path.computeWorldVertices(target, 2, 4, world, 0, 2);
}
addBeforePosition(p, world, 0, out, o);
continue;
} else if (p > pathLength) {
if (prevCurve != AFTER) {
prevCurve = AFTER;
path.computeWorldVertices(target, verticesLength - 6, 4, world, 0);
path.computeWorldVertices(target, verticesLength - 6, 4, world, 0, 2);
}
addAfterPosition(p - pathLength, world, 0, out, o);
continue;
@ -239,10 +239,10 @@ public class PathConstraint implements Constraint {
if (curve != prevCurve) {
prevCurve = curve;
if (closed && curve == curveCount) {
path.computeWorldVertices(target, verticesLength - 4, 4, world, 0);
path.computeWorldVertices(target, 0, 4, world, 4);
path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2);
path.computeWorldVertices(target, 0, 4, world, 4, 2);
} else
path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0);
path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2);
}
addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o,
tangents || (i > 0 && space == 0));
@ -254,15 +254,15 @@ public class PathConstraint implements Constraint {
if (closed) {
verticesLength += 2;
world = this.world.setSize(verticesLength);
path.computeWorldVertices(target, 2, verticesLength - 4, world, 0);
path.computeWorldVertices(target, 0, 2, world, verticesLength - 4);
path.computeWorldVertices(target, 2, verticesLength - 4, world, 0, 2);
path.computeWorldVertices(target, 0, 2, world, verticesLength - 4, 2);
world[verticesLength - 2] = world[0];
world[verticesLength - 1] = world[1];
} else {
curveCount--;
verticesLength -= 4;
world = this.world.setSize(verticesLength);
path.computeWorldVertices(target, 2, verticesLength, world, 0);
path.computeWorldVertices(target, 2, verticesLength, world, 0, 2);
}
// Curve lengths.

View File

@ -33,6 +33,7 @@ package com.esotericsoftware.spine;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.ObjectMap.Entry;
import com.esotericsoftware.spine.Skin.Key;
import com.esotericsoftware.spine.attachments.Attachment;
@ -565,22 +566,30 @@ public class Skeleton {
/** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.
* @param offset An output value, the distance from the skeleton origin to the bottom left corner of the AABB.
* @param size An output value, the width and height of the AABB. */
public void getBounds (Vector2 offset, Vector2 size) {
* @param size An output value, the width and height of the AABB.
* @param temp Working memory. */
public void getBounds (Vector2 offset, Vector2 size, FloatArray temp) {
if (offset == null) throw new IllegalArgumentException("offset cannot be null.");
if (size == null) throw new IllegalArgumentException("size cannot be null.");
Array<Slot> drawOrder = this.drawOrder;
float minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE;
for (int i = 0, n = drawOrder.size; i < n; i++) {
Slot slot = drawOrder.get(i);
int verticesLength = 0;
float[] vertices = null;
Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment)
vertices = ((RegionAttachment)attachment).updateWorldVertices(slot, false);
else if (attachment instanceof MeshAttachment) //
vertices = ((MeshAttachment)attachment).updateWorldVertices(slot, true);
if (attachment instanceof RegionAttachment) {
verticesLength = 8;
vertices = temp.setSize(8);
((RegionAttachment)attachment).computeWorldVertices(slot, vertices, 0, 2);
} else if (attachment instanceof MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)attachment;
verticesLength = mesh.getWorldVerticesLength();
vertices = temp.setSize(verticesLength);
mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2);
}
if (vertices != null) {
for (int ii = 0, nn = vertices.length; ii < nn; ii += 5) {
for (int ii = 0; ii < verticesLength; ii += 2) {
float x = vertices[ii], y = vertices[ii + 1];
minX = Math.min(minX, x);
minY = Math.min(minY, y);

View File

@ -72,7 +72,8 @@ public class SkeletonBounds {
FloatArray polygon = polygonPool.obtain();
polygons.add(polygon);
boundingBox.computeWorldVertices(slot, polygon.setSize(boundingBox.getWorldVerticesLength()));
boundingBox.computeWorldVertices(slot, 0, boundingBox.getWorldVerticesLength(),
polygon.setSize(boundingBox.getWorldVerticesLength()), 0, 2);
}
}

View File

@ -30,9 +30,12 @@
package com.esotericsoftware.spine;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.NumberUtils;
import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.RegionAttachment;
import com.esotericsoftware.spine.attachments.SkeletonAttachment;
@ -41,29 +44,41 @@ import com.esotericsoftware.spine.attachments.MeshAttachment;
public class SkeletonMeshRenderer extends SkeletonRenderer<PolygonSpriteBatch> {
static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0};
private final FloatArray vertices = new FloatArray(32);
@SuppressWarnings("null")
public void draw (PolygonSpriteBatch batch, Skeleton skeleton) {
boolean premultipliedAlpha = this.premultipliedAlpha;
BlendMode blendMode = null;
float[] vertices = null;
int verticesLength = 0;
float[] vertices = null, uvs = null;
short[] triangles = null;
Texture texture = null;
Color color = null, skeletonColor = skeleton.color;
Array<Slot> drawOrder = skeleton.drawOrder;
for (int i = 0, n = drawOrder.size; i < n; i++) {
Slot slot = drawOrder.get(i);
Attachment attachment = slot.attachment;
Texture texture = null;
if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment;
vertices = region.updateWorldVertices(slot, premultipliedAlpha);
verticesLength = 20;
vertices = this.vertices.items;
region.computeWorldVertices(slot, vertices, 0, 5);
triangles = quadTriangles;
texture = region.getRegion().getTexture();
uvs = region.getUVs();
color = region.getColor();
} else if (attachment instanceof MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)attachment;
vertices = mesh.updateWorldVertices(slot, premultipliedAlpha);
verticesLength = (mesh.getWorldVerticesLength() >> 1) * 5;
vertices = this.vertices.setSize(verticesLength);
mesh.computeWorldVertices(slot, 0, mesh.getWorldVerticesLength(), vertices, 0, 5);
triangles = mesh.getTriangles();
texture = mesh.getRegion().getTexture();
uvs = mesh.getUVs();
color = mesh.getColor();
} else if (attachment instanceof SkeletonAttachment) {
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
@ -76,7 +91,7 @@ public class SkeletonMeshRenderer extends SkeletonRenderer<PolygonSpriteBatch> {
attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY());
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
// Set shear.
// Also set shear.
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
attachmentSkeleton.updateWorldTransform();
@ -89,6 +104,18 @@ public class SkeletonMeshRenderer extends SkeletonRenderer<PolygonSpriteBatch> {
}
if (texture != null) {
Color slotColor = slot.getColor();
float alpha = skeletonColor.a * slotColor.a * color.a * 255;
float c = NumberUtils.intToFloatColor(((int)alpha << 24) //
| ((int)(skeletonColor.b * slotColor.b * color.b * alpha) << 16) //
| ((int)(skeletonColor.g * slotColor.g * color.g * alpha) << 8) //
| (int)(skeletonColor.r * slotColor.r * color.r * alpha));
for (int v = 2, u = 0; v < verticesLength; v += 5, u += 2) {
vertices[v] = c;
vertices[v + 1] = uvs[u];
vertices[v + 2] = uvs[u + 1];
}
BlendMode slotBlendMode = slot.data.getBlendMode();
if (slotBlendMode != blendMode) {
blendMode = slotBlendMode;

View File

@ -30,29 +30,47 @@
package com.esotericsoftware.spine;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.NumberUtils;
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.MeshAttachment;
public class SkeletonRenderer<T extends Batch> {
boolean premultipliedAlpha;
private final float[] vertices = new float[20];
public void draw (T batch, Skeleton skeleton) {
boolean premultipliedAlpha = this.premultipliedAlpha;
float[] vertices = this.vertices;
Color skeletonColor = skeleton.color;
Array<Slot> drawOrder = skeleton.drawOrder;
for (int i = 0, n = drawOrder.size; i < n; i++) {
Slot slot = drawOrder.get(i);
Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) {
RegionAttachment regionAttachment = (RegionAttachment)attachment;
float[] vertices = regionAttachment.updateWorldVertices(slot, premultipliedAlpha);
RegionAttachment region = (RegionAttachment)attachment;
region.computeWorldVertices(slot, vertices, 0, 5);
Color color = region.getColor(), slotColor = slot.getColor();
float alpha = skeletonColor.a * slotColor.a * color.a * 255;
float c = NumberUtils.intToFloatColor(((int)alpha << 24) //
| ((int)(skeletonColor.b * slotColor.b * color.b * alpha) << 16) //
| ((int)(skeletonColor.g * slotColor.g * color.g * alpha) << 8) //
| (int)(skeletonColor.r * slotColor.r * color.r * alpha));
float[] uvs = region.getUVs();
for (int u = 0, v = 2; u < 8; u += 2, v += 5) {
vertices[v] = c;
vertices[v + 1] = uvs[u];
vertices[v + 2] = uvs[u + 1];
}
BlendMode blendMode = slot.data.getBlendMode();
batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
batch.draw(regionAttachment.getRegion().getTexture(), vertices, 0, 20);
batch.draw(region.getRegion().getTexture(), vertices, 0, 20);
} else if (attachment instanceof MeshAttachment) {
throw new RuntimeException("SkeletonMeshRenderer is required to render meshes.");

View File

@ -30,8 +30,6 @@
package com.esotericsoftware.spine;
import static com.badlogic.gdx.graphics.g2d.Batch.*;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
@ -56,7 +54,7 @@ public class SkeletonRendererDebug {
private boolean drawBones = true, drawRegionAttachments = true, drawBoundingBoxes = true;
private boolean drawMeshHull = true, drawMeshTriangles = true, drawPaths = true;
private final SkeletonBounds bounds = new SkeletonBounds();
private final FloatArray temp = new FloatArray();
private final FloatArray vertices = new FloatArray(32);
private float scale = 1;
private float boneWidth = 2;
private boolean premultipliedAlpha;
@ -106,12 +104,13 @@ public class SkeletonRendererDebug {
Slot slot = slots.get(i);
Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) {
RegionAttachment regionAttachment = (RegionAttachment)attachment;
float[] vertices = regionAttachment.updateWorldVertices(slot, false);
shapes.line(vertices[X1], vertices[Y1], vertices[X2], vertices[Y2]);
shapes.line(vertices[X2], vertices[Y2], vertices[X3], vertices[Y3]);
shapes.line(vertices[X3], vertices[Y3], vertices[X4], vertices[Y4]);
shapes.line(vertices[X4], vertices[Y4], vertices[X1], vertices[Y1]);
RegionAttachment region = (RegionAttachment)attachment;
float[] vertices = this.vertices.items;
region.computeWorldVertices(slot, vertices, 0, 2);
shapes.line(vertices[0], vertices[1], vertices[2], vertices[3]);
shapes.line(vertices[2], vertices[3], vertices[4], vertices[5]);
shapes.line(vertices[4], vertices[5], vertices[6], vertices[7]);
shapes.line(vertices[6], vertices[7], vertices[0], vertices[1]);
}
}
}
@ -123,14 +122,14 @@ public class SkeletonRendererDebug {
Attachment attachment = slot.attachment;
if (!(attachment instanceof MeshAttachment)) continue;
MeshAttachment mesh = (MeshAttachment)attachment;
mesh.updateWorldVertices(slot, false);
float[] vertices = mesh.getWorldVertices();
float[] vertices = this.vertices.setSize(mesh.getWorldVerticesLength());
mesh.computeWorldVertices(slot, 0, mesh.getWorldVerticesLength(), vertices, 0, 2);
short[] triangles = mesh.getTriangles();
int hullLength = mesh.getHullLength();
if (drawMeshTriangles) {
shapes.setColor(triangleLineColor);
for (int ii = 0, nn = triangles.length; ii < nn; ii += 3) {
int v1 = triangles[ii] * 5, v2 = triangles[ii + 1] * 5, v3 = triangles[ii + 2] * 5;
int v1 = triangles[ii] * 2, v2 = triangles[ii + 1] * 2, v3 = triangles[ii + 2] * 2;
shapes.triangle(vertices[v1], vertices[v1 + 1], //
vertices[v2], vertices[v2 + 1], //
vertices[v3], vertices[v3 + 1] //
@ -139,8 +138,7 @@ public class SkeletonRendererDebug {
}
if (drawMeshHull && hullLength > 0) {
shapes.setColor(attachmentLineColor);
hullLength = (hullLength >> 1) * 5;
float lastX = vertices[hullLength - 5], lastY = vertices[hullLength - 4];
float lastX = vertices[hullLength - 2], lastY = vertices[hullLength - 1];
for (int ii = 0, nn = hullLength; ii < nn; ii += 5) {
float x = vertices[ii], y = vertices[ii + 1];
shapes.line(x, y, lastX, lastY);
@ -173,15 +171,15 @@ public class SkeletonRendererDebug {
if (!(attachment instanceof PathAttachment)) continue;
PathAttachment path = (PathAttachment)attachment;
int nn = path.getWorldVerticesLength();
float[] world = temp.setSize(nn);
path.computeWorldVertices(slot, world);
float[] vertices = this.vertices.setSize(nn);
path.computeWorldVertices(slot, 0, nn, vertices, 0, 2);
Color color = path.getColor();
float x1 = world[2], y1 = world[3], x2 = 0, y2 = 0;
float x1 = vertices[2], y1 = vertices[3], x2 = 0, y2 = 0;
if (path.getClosed()) {
shapes.setColor(color);
float cx1 = world[0], cy1 = world[1], cx2 = world[nn - 2], cy2 = world[nn - 1];
x2 = world[nn - 4];
y2 = world[nn - 3];
float cx1 = vertices[0], cy1 = vertices[1], cx2 = vertices[nn - 2], cy2 = vertices[nn - 1];
x2 = vertices[nn - 4];
y2 = vertices[nn - 3];
shapes.curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, 32);
shapes.setColor(Color.LIGHT_GRAY);
shapes.line(x1, y1, cx1, cy1);
@ -189,9 +187,9 @@ public class SkeletonRendererDebug {
}
nn -= 4;
for (int ii = 4; ii < nn; ii += 6) {
float cx1 = world[ii], cy1 = world[ii + 1], cx2 = world[ii + 2], cy2 = world[ii + 3];
x2 = world[ii + 4];
y2 = world[ii + 5];
float cx1 = vertices[ii], cy1 = vertices[ii + 1], cx2 = vertices[ii + 2], cy2 = vertices[ii + 3];
x2 = vertices[ii + 4];
y2 = vertices[ii + 5];
shapes.setColor(color);
shapes.curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, 32);
shapes.setColor(Color.LIGHT_GRAY);

View File

@ -115,7 +115,7 @@ public class Slot {
/** Vertices to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
* weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions.
* <p>
* See {@link VertexAttachment#computeWorldVertices(Slot, int, int, float[], int)} and {@link DeformTimeline}. */
* See {@link VertexAttachment#computeWorldVertices(Slot, int, int, float[], int, int)} and {@link DeformTimeline}. */
public FloatArray getAttachmentVertices () {
return attachmentVertices;
}

View File

@ -33,12 +33,7 @@ package com.esotericsoftware.spine.attachments;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.NumberUtils;
import com.esotericsoftware.spine.Animation.DeformTimeline;
import com.esotericsoftware.spine.Bone;
import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.Slot;
/** An attachment that displays a textured mesh. A mesh has hull vertices and internal vertices within the hull. Holes are not
* supported. Each vertex has UVs (texture coordinates) and triangles are used to map an image on to the mesh.
@ -47,7 +42,7 @@ import com.esotericsoftware.spine.Slot;
public class MeshAttachment extends VertexAttachment {
private TextureRegion region;
private String path;
private float[] regionUVs, worldVertices;
private float[] regionUVs, uvs;
private short[] triangles;
private final Color color = new Color(1, 1, 1, 1);
private int hullLength;
@ -72,14 +67,9 @@ public class MeshAttachment extends VertexAttachment {
return region;
}
/** Calculates {@link #worldVertices} UVs using {@link #regionUVs} and the {@link #region}. Must be called after changing the
* region UVs or region. */
/** Calculates {@link #uvs} using {@link #regionUVs} and the {@link #region}. Must be called after changing the region UVs or
* region. */
public void updateUVs () {
float[] regionUVs = this.regionUVs;
int verticesLength = regionUVs.length;
int worldVerticesLength = (verticesLength >> 1) * 5;
if (worldVertices == null || worldVertices.length != worldVerticesLength) worldVertices = new float[worldVerticesLength];
float u, v, width, height;
if (region == null) {
u = v = 0;
@ -90,92 +80,28 @@ public class MeshAttachment extends VertexAttachment {
width = region.getU2() - u;
height = region.getV2() - v;
}
float[] regionUVs = this.regionUVs;
if (this.uvs == null || this.uvs.length != regionUVs.length) this.uvs = new float[regionUVs.length];
float[] uvs = this.uvs;
if (region instanceof AtlasRegion && ((AtlasRegion)region).rotate) {
for (int i = 0, w = 3; i < verticesLength; i += 2, w += 5) {
worldVertices[w] = u + regionUVs[i + 1] * width;
worldVertices[w + 1] = v + height - regionUVs[i] * height;
for (int i = 0, n = uvs.length; i < n; i += 2) {
uvs[i] = u + regionUVs[i + 1] * width;
uvs[i + 1] = v + height - regionUVs[i] * height;
}
} else {
for (int i = 0, w = 3; i < verticesLength; i += 2, w += 5) {
worldVertices[w] = u + regionUVs[i] * width;
worldVertices[w + 1] = v + regionUVs[i + 1] * height;
for (int i = 0, n = uvs.length; i < n; i += 2) {
uvs[i] = u + regionUVs[i] * width;
uvs[i + 1] = v + regionUVs[i + 1] * height;
}
}
}
/** @return The updated world vertices. */
public float[] updateWorldVertices (Slot slot, boolean premultipliedAlpha) {
Skeleton skeleton = slot.getSkeleton();
Color skeletonColor = skeleton.getColor(), slotColor = slot.getColor(), meshColor = color;
float alpha = skeletonColor.a * slotColor.a * meshColor.a * 255;
float multiplier = premultipliedAlpha ? alpha : 255;
float color = NumberUtils.intToFloatColor( //
((int)alpha << 24) //
| ((int)(skeletonColor.b * slotColor.b * meshColor.b * multiplier) << 16) //
| ((int)(skeletonColor.g * slotColor.g * meshColor.g * multiplier) << 8) //
| (int)(skeletonColor.r * slotColor.r * meshColor.r * multiplier));
FloatArray deformArray = slot.getAttachmentVertices();
float[] vertices = this.vertices, worldVertices = this.worldVertices;
int[] bones = this.bones;
if (bones == null) {
int verticesLength = vertices.length;
if (deformArray.size > 0) vertices = deformArray.items;
Bone bone = slot.getBone();
float x = bone.getWorldX(), y = bone.getWorldY();
float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD();
for (int v = 0, w = 0; v < verticesLength; v += 2, w += 5) {
float vx = vertices[v], vy = vertices[v + 1];
worldVertices[w] = vx * a + vy * b + x;
worldVertices[w + 1] = vx * c + vy * d + y;
worldVertices[w + 2] = color;
}
return worldVertices;
}
Object[] skeletonBones = skeleton.getBones().items;
if (deformArray.size == 0) {
for (int w = 0, v = 0, b = 0, n = bones.length; v < n; w += 5) {
float wx = 0, wy = 0;
int nn = bones[v++] + v;
for (; v < nn; v++, b += 3) {
Bone bone = (Bone)skeletonBones[bones[v]];
float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight;
wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight;
}
worldVertices[w] = wx;
worldVertices[w + 1] = wy;
worldVertices[w + 2] = color;
}
} else {
float[] deform = deformArray.items;
for (int w = 0, v = 0, b = 0, f = 0, n = bones.length; v < n; w += 5) {
float wx = 0, wy = 0;
int nn = bones[v++] + v;
for (; v < nn; v++, b += 3, f += 2) {
Bone bone = (Bone)skeletonBones[bones[v]];
float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight;
wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight;
}
worldVertices[w] = wx;
worldVertices[w + 1] = wy;
worldVertices[w + 2] = color;
}
}
return worldVertices;
}
/** Returns true if the <code>sourceAttachment</code> is this mesh, else returns true if {@link #inheritDeform} is true and the
* the <code>sourceAttachment</code> is the {@link #parentMesh}. */
public boolean applyDeform (VertexAttachment sourceAttachment) {
return this == sourceAttachment || (inheritDeform && parentMesh == sourceAttachment);
}
public float[] getWorldVertices () {
return worldVertices;
}
/** Triplets of vertex indices which describe the mesh's triangulation. */
public short[] getTriangles () {
return triangles;
@ -185,6 +111,7 @@ public class MeshAttachment extends VertexAttachment {
this.triangles = triangles;
}
/** The UV pair for each vertex, normalized within the texture region. */
public float[] getRegionUVs () {
return regionUVs;
}
@ -194,6 +121,17 @@ public class MeshAttachment extends VertexAttachment {
this.regionUVs = regionUVs;
}
/** The UV pair for each vertex, normalized within the entire texture.
* <p>
* See {@link #updateUVs}. */
public float[] getUVs () {
return uvs;
}
public void setUVs (float[] uvs) {
this.uvs = uvs;
}
/** The color to tint the mesh. */
public Color getColor () {
return color;

View File

@ -30,15 +30,11 @@
package com.esotericsoftware.spine.attachments;
import static com.badlogic.gdx.graphics.g2d.Batch.*;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.NumberUtils;
import com.esotericsoftware.spine.Bone;
import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.Slot;
/** An attachment that displays a textured quadrilateral.
@ -57,7 +53,7 @@ public class RegionAttachment extends Attachment {
private TextureRegion region;
private String path;
private float x, y, scaleX = 1, scaleY = 1, rotation, width, height;
private final float[] vertices = new float[20];
private final float[] uvs = new float[8];
private final float[] offset = new float[8];
private final Color color = new Color(1, 1, 1, 1);
@ -120,25 +116,25 @@ public class RegionAttachment extends Attachment {
public void setRegion (TextureRegion region) {
if (region == null) throw new IllegalArgumentException("region cannot be null.");
this.region = region;
float[] vertices = this.vertices;
float[] uvs = this.uvs;
if (region instanceof AtlasRegion && ((AtlasRegion)region).rotate) {
vertices[U3] = region.getU();
vertices[V3] = region.getV2();
vertices[U4] = region.getU();
vertices[V4] = region.getV();
vertices[U1] = region.getU2();
vertices[V1] = region.getV();
vertices[U2] = region.getU2();
vertices[V2] = region.getV2();
uvs[4] = region.getU();
uvs[5] = region.getV2();
uvs[6] = region.getU();
uvs[7] = region.getV();
uvs[0] = region.getU2();
uvs[1] = region.getV();
uvs[2] = region.getU2();
uvs[3] = region.getV2();
} else {
vertices[U2] = region.getU();
vertices[V2] = region.getV2();
vertices[U3] = region.getU();
vertices[V3] = region.getV();
vertices[U4] = region.getU2();
vertices[V4] = region.getV();
vertices[U1] = region.getU2();
vertices[V1] = region.getV2();
uvs[2] = region.getU();
uvs[3] = region.getV2();
uvs[4] = region.getU();
uvs[5] = region.getV();
uvs[6] = region.getU2();
uvs[7] = region.getV();
uvs[0] = region.getU2();
uvs[1] = region.getV2();
}
}
@ -147,55 +143,42 @@ public class RegionAttachment extends Attachment {
return region;
}
/** @return The updated world vertices. */
public float[] updateWorldVertices (Slot slot, boolean premultipliedAlpha) {
Skeleton skeleton = slot.getSkeleton();
Color skeletonColor = skeleton.getColor();
Color slotColor = slot.getColor();
Color regionColor = color;
float alpha = skeletonColor.a * slotColor.a * regionColor.a * 255;
float multiplier = premultipliedAlpha ? alpha : 255;
float color = NumberUtils.intToFloatColor( //
((int)alpha << 24) //
| ((int)(skeletonColor.b * slotColor.b * regionColor.b * multiplier) << 16) //
| ((int)(skeletonColor.g * slotColor.g * regionColor.g * multiplier) << 8) //
| (int)(skeletonColor.r * slotColor.r * regionColor.r * multiplier));
float[] vertices = this.vertices;
float[] offset = this.offset;
/** Transforms the attachment's four vertices to world coordinates.
* <p>
* See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
* Runtimes Guide.
* @param worldVertices The output world vertices. Must have a length >= <code>offset</code> + 8.
* @param offset The <code>worldVertices</code> index to begin writing values.
* @param stride The number of <code>worldVertices</code> entries between the value pairs written. */
public void computeWorldVertices (Slot slot, float[] worldVertices, int offset, int stride) {
float[] vertexOffset = this.offset;
Bone bone = slot.getBone();
float x = bone.getWorldX(), y = bone.getWorldY();
float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD();
float offsetX, offsetY;
offsetX = offset[BRX];
offsetY = offset[BRY];
vertices[X1] = offsetX * a + offsetY * b + x; // br
vertices[Y1] = offsetX * c + offsetY * d + y;
vertices[C1] = color;
offsetX = vertexOffset[BRX];
offsetY = vertexOffset[BRY];
worldVertices[offset] = offsetX * a + offsetY * b + x; // br
worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
offset += stride;
offsetX = offset[BLX];
offsetY = offset[BLY];
vertices[X2] = offsetX * a + offsetY * b + x; // bl
vertices[Y2] = offsetX * c + offsetY * d + y;
vertices[C2] = color;
offsetX = vertexOffset[BLX];
offsetY = vertexOffset[BLY];
worldVertices[offset] = offsetX * a + offsetY * b + x; // bl
worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
offset += stride;
offsetX = offset[ULX];
offsetY = offset[ULY];
vertices[X3] = offsetX * a + offsetY * b + x; // ul
vertices[Y3] = offsetX * c + offsetY * d + y;
vertices[C3] = color;
offsetX = vertexOffset[ULX];
offsetY = vertexOffset[ULY];
worldVertices[offset] = offsetX * a + offsetY * b + x; // ul
worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
offset += stride;
offsetX = offset[URX];
offsetY = offset[URY];
vertices[X4] = offsetX * a + offsetY * b + x; // ur
vertices[Y4] = offsetX * c + offsetY * d + y;
vertices[C4] = color;
return vertices;
}
public float[] getWorldVertices () {
return vertices;
offsetX = vertexOffset[URX];
offsetY = vertexOffset[URY];
worldVertices[offset] = offsetX * a + offsetY * b + x; // ur
worldVertices[offset + 1] = offsetX * c + offsetY * d + y;
}
/** For each of the 4 vertices, a pair of <code>x,y</code> values that is the local position of the vertex.
@ -205,6 +188,10 @@ public class RegionAttachment extends Attachment {
return offset;
}
public float[] getUVs () {
return uvs;
}
/** The local x translation. */
public float getX () {
return x;

View File

@ -1,100 +0,0 @@
/******************************************************************************
* 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 com.esotericsoftware.spine.attachments;
import com.esotericsoftware.spine.Slot;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
/** Attachment that displays various texture regions over time. */
public class RegionSequenceAttachment extends RegionAttachment {
private Mode mode;
private float frameTime;
private TextureRegion[] regions;
public RegionSequenceAttachment (String name) {
super(name);
}
public float[] updateWorldVertices (Slot slot, boolean premultipliedAlpha) {
if (regions == null) throw new IllegalStateException("Regions have not been set: " + this);
int frameIndex = (int)(slot.getAttachmentTime() / frameTime);
switch (mode) {
case forward:
frameIndex = Math.min(regions.length - 1, frameIndex);
break;
case forwardLoop:
frameIndex = frameIndex % regions.length;
break;
case pingPong:
frameIndex = frameIndex % (regions.length << 1);
if (frameIndex >= regions.length) frameIndex = regions.length - 1 - (frameIndex - regions.length);
break;
case random:
frameIndex = MathUtils.random(regions.length - 1);
break;
case backward:
frameIndex = Math.max(regions.length - frameIndex - 1, 0);
break;
case backwardLoop:
frameIndex = frameIndex % regions.length;
frameIndex = regions.length - frameIndex - 1;
break;
}
setRegion(regions[frameIndex]);
return super.updateWorldVertices(slot, premultipliedAlpha);
}
public TextureRegion[] getRegions () {
if (regions == null) throw new IllegalStateException("Regions have not been set: " + this);
return regions;
}
public void setRegions (TextureRegion[] regions) {
this.regions = regions;
}
/** Sets the time in seconds each frame is shown. */
public void setFrameTime (float frameTime) {
this.frameTime = frameTime;
}
public void setMode (Mode mode) {
this.mode = mode;
}
static public enum Mode {
forward, backward, forwardLoop, backwardLoop, pingPong, random
}
}

View File

@ -46,14 +46,6 @@ public class VertexAttachment extends Attachment {
super(name);
}
/** Transforms the attachment's local {@link #getVertices()} to world coordinates, using 0 for <code>start</code> and
* <code>offset</code>.
* <p>
* See {@link #computeWorldVertices(Slot, int, int, float[], int)}. */
public void computeWorldVertices (Slot slot, float[] worldVertices) {
computeWorldVertices(slot, 0, worldVerticesLength, worldVertices, 0);
}
/** Transforms the attachment's local {@link #getVertices()} to world coordinates. If the slot has
* {@link Slot#getAttachmentVertices()}, they are used to deform the vertices.
* <p>
@ -62,9 +54,10 @@ public class VertexAttachment extends Attachment {
* @param start The index of the first {@link #getVertices()} value to transform. Each vertex has 2 values, x and y.
* @param count The number of world vertex values to output. Must be <= {@link #getWorldVerticesLength()} - <code>start</code>.
* @param worldVertices The output world vertices. Must have a length >= <code>offset</code> + <code>count</code>.
* @param offset The <code>worldVertices</code> index to begin writing values. */
public void computeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset) {
count += offset;
* @param offset The <code>worldVertices</code> index to begin writing values.
* @param stride The number of <code>worldVertices</code> entries between the value pairs written. */
public void computeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset, int stride) {
count = offset + (count >> 1) * stride;
Skeleton skeleton = slot.getSkeleton();
FloatArray deformArray = slot.getAttachmentVertices();
float[] vertices = this.vertices;
@ -74,7 +67,7 @@ public class VertexAttachment extends Attachment {
Bone bone = slot.getBone();
float x = bone.getWorldX(), y = bone.getWorldY();
float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD();
for (int v = start, w = offset; w < count; v += 2, w += 2) {
for (int v = start, w = offset; w < count; v += 2, w += stride) {
float vx = vertices[v], vy = vertices[v + 1];
worldVertices[w] = vx * a + vy * b + x;
worldVertices[w + 1] = vx * c + vy * d + y;
@ -89,7 +82,7 @@ public class VertexAttachment extends Attachment {
}
Object[] skeletonBones = skeleton.getBones().items;
if (deformArray.size == 0) {
for (int w = offset, b = skip * 3; w < count; w += 2) {
for (int w = offset, b = skip * 3; w < count; w += stride) {
float wx = 0, wy = 0;
int n = bones[v++];
n += v;
@ -104,7 +97,7 @@ public class VertexAttachment extends Attachment {
}
} else {
float[] deform = deformArray.items;
for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) {
for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) {
float wx = 0, wy = 0;
int n = bones[v++];
n += v;
@ -150,7 +143,7 @@ public class VertexAttachment extends Attachment {
}
/** The maximum length required of the <code>worldVertices</code> passed to
* {@link #computeWorldVertices(Slot, int, int, float[], int)}. */
* {@link #computeWorldVertices(Slot, int, int, float[], int, int)}. */
public int getWorldVerticesLength () {
return worldVerticesLength;
}