Refactored skin API, see #841. This commit is missing Skin#copy(Skin) which has to handle linked mesh attachments properly.

This commit is contained in:
badlogic 2019-04-30 15:34:44 +02:00
parent 6205e51752
commit 86c3aa02ba
14 changed files with 179 additions and 59 deletions

View File

@ -54,8 +54,8 @@ public class AttachmentTimelineTests {
Attachment attachment2 = new Attachment("attachment2") {}; Attachment attachment2 = new Attachment("attachment2") {};
Skin skin = new Skin("skin"); Skin skin = new Skin("skin");
skin.addAttachment(0, "attachment1", attachment1); skin.setAttachment(0, "attachment1", attachment1);
skin.addAttachment(0, "attachment2", attachment2); skin.setAttachment(0, "attachment2", attachment2);
skeletonData.setDefaultSkin(skin); skeletonData.setDefaultSkin(skin);
skeleton = new Skeleton(skeletonData); skeleton = new Skeleton(skeletonData);

View File

@ -30,15 +30,14 @@
package com.esotericsoftware.spine; package com.esotericsoftware.spine;
import static com.esotericsoftware.spine.utils.SpineUtils.*; import static com.esotericsoftware.spine.utils.SpineUtils.cosDeg;
import static com.esotericsoftware.spine.utils.SpineUtils.sinDeg;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.ObjectMap.Entry; import com.esotericsoftware.spine.Skin.SkinEntry;
import com.esotericsoftware.spine.Skin.Key;
import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.MeshAttachment; import com.esotericsoftware.spine.attachments.MeshAttachment;
import com.esotericsoftware.spine.attachments.PathAttachment; import com.esotericsoftware.spine.attachments.PathAttachment;
@ -279,8 +278,8 @@ public class Skeleton {
} }
private void sortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) { private void sortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
for (Entry<Key, Attachment> entry : skin.attachments.entries()) for (SkinEntry entry : skin.attachments.keys())
if (entry.key.slotIndex == slotIndex) sortPathConstraintAttachment(entry.value, slotBone); if (entry.getSlotIndex() == slotIndex) sortPathConstraintAttachment(entry.getAttachment(), slotBone);
} }
private void sortPathConstraintAttachment (Attachment attachment, Bone slotBone) { private void sortPathConstraintAttachment (Attachment attachment, Bone slotBone) {

View File

@ -362,7 +362,7 @@ public class SkeletonBinary {
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
String name = input.readString(); String name = input.readString();
Attachment attachment = readAttachment(input, skeletonData, skin, slotIndex, name, nonessential); Attachment attachment = readAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
if (attachment != null) skin.addAttachment(slotIndex, name, attachment); if (attachment != null) skin.setAttachment(slotIndex, name, attachment);
} }
} }
return skin; return skin;

View File

@ -294,7 +294,7 @@ public class SkeletonJson {
for (JsonValue entry = slotEntry.child; entry != null; entry = entry.next) { for (JsonValue entry = slotEntry.child; entry != null; entry = entry.next) {
try { try {
Attachment attachment = readAttachment(entry, skin, slot.index, entry.name, skeletonData); Attachment attachment = readAttachment(entry, skin, slot.index, entry.name, skeletonData);
if (attachment != null) skin.addAttachment(slot.index, entry.name, attachment); if (attachment != null) skin.setAttachment(slot.index, entry.name, attachment);
} catch (Throwable ex) { } catch (Throwable ex) {
throw new SerializationException("Error reading attachment: " + entry.name + ", skin: " + skin, ex); throw new SerializationException("Error reading attachment: " + entry.name + ", skin: " + skin, ex);
} }

View File

@ -32,9 +32,6 @@ package com.esotericsoftware.spine;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectMap.Entry;
import com.badlogic.gdx.utils.Pool;
import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.Attachment;
/** Stores attachments by slot index and attachment name. /** Stores attachments by slot index and attachment name.
@ -43,15 +40,10 @@ import com.esotericsoftware.spine.attachments.Attachment;
* <a href="http://esotericsoftware.com/spine-runtime-skins">Runtime skins</a> in the Spine Runtimes Guide. */ * <a href="http://esotericsoftware.com/spine-runtime-skins">Runtime skins</a> in the Spine Runtimes Guide. */
public class Skin { public class Skin {
final String name; final String name;
final ObjectMap<Key, Attachment> attachments = new ObjectMap(); final ObjectMap<SkinEntry, Attachment> attachments = new ObjectMap();
final Array<BoneData> bones = new Array(); final Array<BoneData> bones = new Array();
final Array<ConstraintData> constraints = new Array(); final Array<ConstraintData> constraints = new Array();
private final Key lookup = new Key(); private final SkinEntry lookup = new SkinEntry();
final Pool<Key> keyPool = new Pool(64) {
protected Object newObject () {
return new Key();
}
};
public Skin (String name) { public Skin (String name) {
if (name == null) throw new IllegalArgumentException("name cannot be null."); if (name == null) throw new IllegalArgumentException("name cannot be null.");
@ -59,53 +51,49 @@ public class Skin {
} }
/** Adds an attachment to the skin for the specified slot index and name. */ /** Adds an attachment to the skin for the specified slot index and name. */
public void addAttachment (int slotIndex, String name, Attachment attachment) { public void setAttachment (int slotIndex, String name, Attachment attachment) {
if (attachment == null) throw new IllegalArgumentException("attachment cannot be null."); if (attachment == null) throw new IllegalArgumentException("attachment cannot be null.");
if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0.");
Key key = keyPool.obtain(); attachments.put(new SkinEntry(slotIndex, name, attachment), attachment);
key.set(slotIndex, name);
attachments.put(key, attachment);
} }
/** Adds all attachments from the specified skin to this skin. */ /** Adds all attachments from the specified skin to this skin. */
public void addAttachments (Skin skin) { public void addSkin (Skin skin) {
for (Entry<Key, Attachment> entry : skin.attachments.entries()) for (SkinEntry entry : skin.attachments.keys())
addAttachment(entry.key.slotIndex, entry.key.name, entry.value); setAttachment(entry.getSlotIndex(), entry.getName(), entry.getAttachment());
} }
/** Returns the attachment for the specified slot index and name, or null. */ /** Returns the attachment for the specified slot index and name, or null. */
public Attachment getAttachment (int slotIndex, String name) { public Attachment getAttachment (int slotIndex, String name) {
if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0.");
lookup.set(slotIndex, name); lookup.set(slotIndex, name, null);
return attachments.get(lookup); return attachments.get(lookup);
} }
/** Removes the attachment in the skin for the specified slot index and name, if any. */ /** Removes the attachment in the skin for the specified slot index and name, if any. */
public void removeAttachment (int slotIndex, String name) { public void removeAttachment (int slotIndex, String name) {
if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0.");
Key key = keyPool.obtain(); lookup.set(slotIndex, name, null);
key.set(slotIndex, name); attachments.remove(lookup);
attachments.remove(key);
keyPool.free(key);
} }
public void findNamesForSlot (int slotIndex, Array<String> names) { /** Returns all attachments contained in this skin. */
if (names == null) throw new IllegalArgumentException("names cannot be null."); public Array<SkinEntry> getAttachments () {
if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); Array<SkinEntry> entries = new Array();
for (Key key : attachments.keys()) for (SkinEntry entry : this.attachments.keys())
if (key.slotIndex == slotIndex) names.add(key.name); entries.add(entry);
return entries;
} }
public void findAttachmentsForSlot (int slotIndex, Array<Attachment> attachments) { /** Returns all {@link SkinEntry} instances for the given slot contained in this skin. */
if (attachments == null) throw new IllegalArgumentException("attachments cannot be null."); public Array<SkinEntry> getEntries (int slotIndex) {
if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); Array<SkinEntry> entries = new Array();
for (Entry<Key, Attachment> entry : this.attachments.entries()) for (SkinEntry entry : this.attachments.keys())
if (entry.key.slotIndex == slotIndex) attachments.add(entry.value); if (entry.getSlotIndex() == slotIndex) entries.add(entry);
return entries;
} }
public void clear () { public void clear () {
for (Key key : attachments.keys())
keyPool.free(key);
attachments.clear(1024); attachments.clear(1024);
bones.clear(); bones.clear();
constraints.clear(); constraints.clear();
@ -134,26 +122,49 @@ public class Skin {
/** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */ /** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */
void attachAll (Skeleton skeleton, Skin oldSkin) { void attachAll (Skeleton skeleton, Skin oldSkin) {
for (Entry<Key, Attachment> entry : oldSkin.attachments.entries()) { for (SkinEntry entry : oldSkin.attachments.keys()) {
int slotIndex = entry.key.slotIndex; int slotIndex = entry.getSlotIndex();
Slot slot = skeleton.slots.get(slotIndex); Slot slot = skeleton.slots.get(slotIndex);
if (slot.attachment == entry.value) { if (slot.attachment == entry.getAttachment()) {
Attachment attachment = getAttachment(slotIndex, entry.key.name); Attachment attachment = getAttachment(slotIndex, entry.getName());
if (attachment != null) slot.setAttachment(attachment); if (attachment != null) slot.setAttachment(attachment);
} }
} }
} }
static class Key { /** Stores an entry in the skin consisting of the slot index, name, and attachment **/
int slotIndex; public static class SkinEntry {
String name; private int slotIndex;
int hashCode; private String name;
private Attachment attachment;
private int hashCode;
public void set (int slotIndex, String name) { SkinEntry () {
set(0, "", null);
}
SkinEntry (int slotIndex, String name, Attachment attachment) {
set(slotIndex, name, attachment);
}
void set (int slotIndex, String name, Attachment attachment) {
if (name == null) throw new IllegalArgumentException("name cannot be null."); if (name == null) throw new IllegalArgumentException("name cannot be null.");
this.slotIndex = slotIndex; this.slotIndex = slotIndex;
this.name = name; this.name = name;
hashCode = name.hashCode() + slotIndex * 37; this.attachment = attachment;
this.hashCode = name.hashCode() + slotIndex * 37;
}
public int getSlotIndex () {
return slotIndex;
}
public String getName () {
return name;
}
public Attachment getAttachment () {
return attachment;
} }
public int hashCode () { public int hashCode () {
@ -162,7 +173,7 @@ public class Skin {
public boolean equals (Object object) { public boolean equals (Object object) {
if (object == null) return false; if (object == null) return false;
Key other = (Key)object; SkinEntry other = (SkinEntry)object;
if (slotIndex != other.slotIndex) return false; if (slotIndex != other.slotIndex) return false;
if (!name.equals(other.name)) return false; if (!name.equals(other.name)) return false;
return true; return true;

View File

@ -47,4 +47,7 @@ abstract public class Attachment {
public String toString () { public String toString () {
return getName(); return getName();
} }
/** Returns a copy of the attachment. **/
public abstract Attachment copy ();
} }

View File

@ -51,4 +51,11 @@ public class BoundingBoxAttachment extends VertexAttachment {
public Color getColor () { public Color getColor () {
return color; return color;
} }
public Attachment copy () {
BoundingBoxAttachment copy = new BoundingBoxAttachment(name);
copyTo(copy);
copy.color.set(color);
return copy;
}
} }

View File

@ -59,4 +59,12 @@ public class ClippingAttachment extends VertexAttachment {
public Color getColor () { public Color getColor () {
return color; return color;
} }
public Attachment copy () {
ClippingAttachment copy = new ClippingAttachment(name);
copyTo(copy);
copy.endSlot = endSlot;
copy.color.set(color);
return copy;
}
} }

View File

@ -33,7 +33,6 @@ package com.esotericsoftware.spine.attachments;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.esotericsoftware.spine.Animation.DeformTimeline; import com.esotericsoftware.spine.Animation.DeformTimeline;
/** An attachment that displays a textured mesh. A mesh has hull vertices and internal vertices within the hull. Holes are not /** An attachment that displays a textured mesh. A mesh has hull vertices and internal vertices within the hull. Holes are not
@ -253,4 +252,35 @@ public class MeshAttachment extends VertexAttachment {
public void setInheritDeform (boolean inheritDeform) { public void setInheritDeform (boolean inheritDeform) {
this.inheritDeform = inheritDeform; this.inheritDeform = inheritDeform;
} }
public Attachment copy () {
MeshAttachment copy = new MeshAttachment(name);
copy.region = region;
copy.path = path;
if (parentMesh == null) {
copyTo(copy);
copy.regionUVs = new float[regionUVs.length];
System.arraycopy(regionUVs, 0, copy.regionUVs, 0, regionUVs.length);
copy.uvs = new float[uvs.length];
System.arraycopy(uvs, 0, copy.uvs, 0, uvs.length);
copy.triangles = new short[triangles.length];
System.arraycopy(triangles, 0, copy.triangles, 0, triangles.length);
copy.color.set(color);
copy.hullLength = hullLength;
copy.inheritDeform = inheritDeform;
// Nonessential.
if (edges != null) {
copy.edges = new short[edges.length];
System.arraycopy(edges, 0, copy.edges, 0, edges.length);
}
copy.width = width;
copy.height = height;
} else
copy.setParentMesh(parentMesh);
return copy;
}
} }

View File

@ -80,4 +80,14 @@ public class PathAttachment extends VertexAttachment {
public Color getColor () { public Color getColor () {
return color; return color;
} }
public Attachment copy () {
PathAttachment copy = new PathAttachment(name);
copyTo(copy);
copy.lengths = new float[lengths.length];
System.arraycopy(lengths, 0, copy.lengths, 0, lengths.length);
copy.closed = closed;
copy.constantSpeed = constantSpeed;
return copy;
}
} }

View File

@ -30,7 +30,9 @@
package com.esotericsoftware.spine.attachments; package com.esotericsoftware.spine.attachments;
import static com.badlogic.gdx.math.MathUtils.*; import static com.badlogic.gdx.math.MathUtils.cosDeg;
import static com.badlogic.gdx.math.MathUtils.radDeg;
import static com.badlogic.gdx.math.MathUtils.sinDeg;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
@ -93,4 +95,14 @@ public class PointAttachment extends Attachment {
float y = cos * bone.getC() + sin * bone.getD(); float y = cos * bone.getC() + sin * bone.getD();
return (float)Math.atan2(y, x) * radDeg; return (float)Math.atan2(y, x) * radDeg;
} }
public Attachment copy () {
PointAttachment copy = new PointAttachment(name);
copy.name = name;
copy.x = x;
copy.y = y;
copy.rotation = rotation;
copy.color.set(color);
return copy;
}
} }

View File

@ -264,4 +264,21 @@ public class RegionAttachment extends Attachment {
public void setPath (String path) { public void setPath (String path) {
this.path = path; this.path = path;
} }
public Attachment copy () {
RegionAttachment copy = new RegionAttachment(name);
copy.region = region;
copy.path = path;
copy.x = x;
copy.y = y;
copy.scaleX = scaleX;
copy.scaleY = scaleY;
copy.rotation = rotation;
copy.width = width;
copy.height = height;
System.arraycopy(uvs, 0, copy.uvs, 0, uvs.length);
System.arraycopy(offset, 0, copy.offset, 0, offset.length);
copy.color.set(color);
return copy;
}
} }

View File

@ -49,4 +49,10 @@ public class SkeletonAttachment extends Attachment {
public void setSkeleton (Skeleton skeleton) { public void setSkeleton (Skeleton skeleton) {
this.skeleton = skeleton; this.skeleton = skeleton;
} }
public Attachment copy () {
SkeletonAttachment copy = new SkeletonAttachment(name);
copy.skeleton = skeleton;
return copy;
}
} }

View File

@ -31,14 +31,13 @@
package com.esotericsoftware.spine.attachments; package com.esotericsoftware.spine.attachments;
import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.FloatArray;
import com.esotericsoftware.spine.Bone; import com.esotericsoftware.spine.Bone;
import com.esotericsoftware.spine.Skeleton; import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.Slot; import com.esotericsoftware.spine.Slot;
/** Base class for an attachment with vertices that are transformed by one or more bones and can be deformed by a slot's /** Base class for an attachment with vertices that are transformed by one or more bones and can be deformed by a slot's
* {@link Slot#getDeform()}. */ * {@link Slot#getDeform()}. */
public class VertexAttachment extends Attachment { public abstract class VertexAttachment extends Attachment {
static private int nextID; static private int nextID;
private final int id = (nextID() & 65535) << 11; private final int id = (nextID() & 65535) << 11;
@ -162,6 +161,24 @@ public class VertexAttachment extends Attachment {
return id; return id;
} }
/** Internal method used by VertexAttachment subclasses to copy basic data. Does not copy id (generated) and name (set on
* construction). **/
void copyTo (VertexAttachment attachment) {
if (bones != null) {
attachment.bones = new int[bones.length];
System.arraycopy(bones, 0, attachment.bones, 0, bones.length);
} else
attachment.bones = null;
if (vertices != null) {
attachment.vertices = new float[vertices.length];
System.arraycopy(vertices, 0, attachment.vertices, 0, vertices.length);
} else
attachment.vertices = null;
attachment.worldVerticesLength = worldVerticesLength;
}
static private synchronized int nextID () { static private synchronized int nextID () {
return nextID++; return nextID++;
} }