diff --git a/spine-csharp/src/Atlas.cs b/spine-csharp/src/Atlas.cs index 4f1137cd5..7ab0af481 100644 --- a/spine-csharp/src/Atlas.cs +++ b/spine-csharp/src/Atlas.cs @@ -28,125 +28,111 @@ using System.IO; namespace Spine { public class Atlas { - public Format Format; - public TextureFilter MinFilter; - public TextureFilter MagFilter; - public TextureWrap UWrap; - public TextureWrap VWrap; - public int TextureWidth; - public int TextureHeight; - public List Regions; - public Object Texture; + List pages = new List(); + List regions = new List(); - public Atlas (String path, Object texture, int textureWidth, int textureHeight) { + public Atlas (String path, TextureLoader textureLoader) { using (StreamReader reader = new StreamReader(path)) { try { - initialize(reader, texture, textureWidth, textureHeight); + Load(reader, Path.GetDirectoryName(path), textureLoader); } catch (Exception ex) { throw new Exception("Error reading atlas file: " + path, ex); } } } - public Atlas (TextReader reader, Object texture, int textureWidth, int textureHeight) { - initialize(reader, texture, textureWidth, textureHeight); + public Atlas (TextReader reader, String dir, TextureLoader textureLoader) { + Load(reader, dir, textureLoader); } - private void initialize (TextReader reader, Object texture, int textureWidth, int textureHeight) { - TextureWidth = textureWidth; - TextureHeight = textureHeight; - Texture = texture; + private void Load (TextReader reader, String imagesDir, TextureLoader textureLoader) { + if (textureLoader == null) throw new ArgumentNullException("textureLoader cannot be null."); - Regions = new List(); - float invTexWidth = 1f / textureWidth; - float invTexHeight = 1f / textureHeight; String[] tuple = new String[4]; - - // Skip past first page name. + AtlasPage page = null; while (true) { String line = reader.ReadLine(); - if (line.Trim().Length != 0) - break; - } + if (line == null) break; + if (line.Trim().Length == 0) + page = null; + else if (page == null) { + page = new AtlasPage(); + page.name = line; - Format = (Format)Enum.Parse(typeof(Format), readValue(reader), false); + page.format = (Format)Enum.Parse(typeof(Format), readValue(reader), false); - readTuple(reader, tuple); - MinFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0]); - MagFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1]); + readTuple(reader, tuple); + page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0]); + page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1]); - String direction = readValue(reader); - UWrap = TextureWrap.ClampToEdge; - VWrap = TextureWrap.ClampToEdge; - if (direction == "x") - UWrap = TextureWrap.Repeat; - else if (direction == "y") - VWrap = TextureWrap.Repeat; - else if (direction == "xy") - UWrap = VWrap = TextureWrap.Repeat; + String direction = readValue(reader); + page.uWrap = TextureWrap.ClampToEdge; + page.vWrap = TextureWrap.ClampToEdge; + if (direction == "x") + page.uWrap = TextureWrap.Repeat; + else if (direction == "y") + page.vWrap = TextureWrap.Repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = TextureWrap.Repeat; - while (true) { - String line = reader.ReadLine(); - if (line == null || line.Trim().Length == 0) break; + textureLoader.Load(page, Path.Combine(imagesDir, line)); - AtlasRegion region = new AtlasRegion(); - region.Atlas = this; - region.Name = line; + pages.Add(page); - region.Rotate = Boolean.Parse(readValue(reader)); + } else { + AtlasRegion region = new AtlasRegion(); + region.name = line; + region.page = page; - readTuple(reader, tuple); - int x = int.Parse(tuple[0]); - int y = int.Parse(tuple[1]); + region.rotate = Boolean.Parse(readValue(reader)); - readTuple(reader, tuple); - int width = int.Parse(tuple[0]); - int height = int.Parse(tuple[1]); + readTuple(reader, tuple); + int x = int.Parse(tuple[0]); + int y = int.Parse(tuple[1]); - region.U = x * invTexWidth; - region.V = y * invTexHeight; - region.U2 = (x + width) * invTexWidth; - region.V2 = (y + height) * invTexHeight; - region.Width = Math.Abs(width); - region.Height = Math.Abs(height); + readTuple(reader, tuple); + int width = int.Parse(tuple[0]); + int height = int.Parse(tuple[1]); - if (readTuple(reader, tuple) == 4) { // split is optional - region.Splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), + region.u = x / (float)page.width; + region.v = y / (float)page.height; + region.u2 = (x + width) / (float)page.width; + region.v2 = (y + height) / (float)page.height; + region.x = x; + region.y = y; + region.width = Math.Abs(width); + region.height = Math.Abs(height); + + if (readTuple(reader, tuple) == 4) { // split is optional + region.splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), int.Parse(tuple[2]), int.Parse(tuple[3])}; - if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits - region.Pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), + if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits + region.pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), int.Parse(tuple[2]), int.Parse(tuple[3])}; - readTuple(reader, tuple); + readTuple(reader, tuple); + } } + + region.originalWidth = int.Parse(tuple[0]); + region.originalHeight = int.Parse(tuple[1]); + + readTuple(reader, tuple); + region.offsetX = int.Parse(tuple[0]); + region.offsetY = int.Parse(tuple[1]); + + region.index = int.Parse(readValue(reader)); + + regions.Add(region); } - - region.OriginalWidth = int.Parse(tuple[0]); - region.OriginalHeight = int.Parse(tuple[1]); - - readTuple(reader, tuple); - region.OffsetX = int.Parse(tuple[0]); - region.OffsetY = int.Parse(tuple[1]); - - region.Index = int.Parse(readValue(reader)); - - Regions.Add(region); - } - - while (true) { - String line = reader.ReadLine(); - if (line == null) - break; - if (line.Trim().Length != 0) throw new Exception("An atlas with multiple images is not supported."); } } static String readValue (TextReader reader) { String line = reader.ReadLine(); int colon = line.IndexOf(':'); - if (colon == -1) - throw new Exception("Invalid line: " + line); + if (colon == -1) throw new Exception("Invalid line: " + line); return line.Substring(colon + 1).Trim(); } @@ -154,14 +140,12 @@ namespace Spine { static int readTuple (TextReader reader, String[] tuple) { String line = reader.ReadLine(); int colon = line.IndexOf(':'); - if (colon == -1) - throw new Exception("Invalid line: " + line); + if (colon == -1) throw new Exception("Invalid line: " + line); int i = 0, lastMatch = colon + 1; - for (i = 0; i < 3; i++) { + for (; i < 3; i++) { int comma = line.IndexOf(',', lastMatch); if (comma == -1) { - if (i == 0) - throw new Exception("Invalid line: " + line); + if (i == 0) throw new Exception("Invalid line: " + line); break; } tuple[i] = line.Substring(lastMatch, comma - lastMatch).Trim(); @@ -175,9 +159,8 @@ namespace Spine { * should be cached rather than calling this method multiple times. * @return The region, or null. */ public AtlasRegion FindRegion (String name) { - for (int i = 0, n = Regions.Count; i < n; i++) - if (Regions[i].Name == name) - return Regions[i]; + for (int i = 0, n = regions.Count; i < n; i++) + if (regions[i].name == name) return regions[i]; return null; } } @@ -208,17 +191,31 @@ namespace Spine { Repeat } + public class AtlasPage { + public String name; + public Format format; + public TextureFilter minFilter; + public TextureFilter magFilter; + public TextureWrap uWrap; + public TextureWrap vWrap; + public Object texture; + public int width, height; + } + public class AtlasRegion { - public Atlas Atlas; - public float U, V; - public float U2, V2; - public int Width, Height; - public int Index; - public String Name; - public float OffsetX, OffsetY; - public int OriginalWidth, OriginalHeight; - public bool Rotate; - public int[] Splits; - public int[] Pads; + public AtlasPage page; + public String name; + public int x, y, width, height; + public float u, v, u2, v2; + public float offsetX, offsetY; + public int originalWidth, originalHeight; + public int index; + public bool rotate; + public int[] splits; + public int[] pads; + } + + public interface TextureLoader { + void Load (AtlasPage page, String path); } } diff --git a/spine-csharp/src/Attachments/RegionAttachment.cs b/spine-csharp/src/Attachments/RegionAttachment.cs index 0226b04a7..11a7cf95e 100644 --- a/spine-csharp/src/Attachments/RegionAttachment.cs +++ b/spine-csharp/src/Attachments/RegionAttachment.cs @@ -47,43 +47,12 @@ namespace Spine { public float[] Offset { get; private set; } public float[] Vertices { get; private set; } - public float[] UVs { get; private set; } - - private AtlasRegion region; - public AtlasRegion Region { - get { - return region; - } - set { - region = value; - float[] uvs = UVs; - if (value.Rotate) { - uvs[X2] = value.U; - uvs[Y2] = value.V2; - uvs[X3] = value.U; - uvs[Y3] = value.V; - uvs[X4] = value.U2; - uvs[Y4] = value.V; - uvs[X1] = value.U2; - uvs[Y1] = value.V2; - } else { - uvs[X1] = value.U; - uvs[Y1] = value.V2; - uvs[X2] = value.U; - uvs[Y2] = value.V; - uvs[X3] = value.U2; - uvs[Y3] = value.V; - uvs[X4] = value.U2; - uvs[Y4] = value.V2; - } - } - } + public AtlasRegion Region { get; set; } public RegionAttachment (string name) : base(name) { Offset = new float[8]; Vertices = new float[8]; - UVs = new float[8]; ScaleX = 1; ScaleY = 1; } @@ -97,16 +66,16 @@ namespace Spine { float localY = -localY2; AtlasRegion region = Region; if (region != null) { - if (region.Rotate) { - localX += region.OffsetX / region.OriginalWidth * height; - localY += region.OffsetY / region.OriginalHeight * width; - localX2 -= (region.OriginalWidth - region.OffsetX - region.Height) / region.OriginalWidth * width; - localY2 -= (region.OriginalHeight - region.OffsetY - region.Width) / region.OriginalHeight * height; + if (region.rotate) { + localX += region.offsetX / region.originalWidth * height; + localY += region.offsetY / region.originalHeight * width; + localX2 -= (region.originalWidth - region.offsetX - region.height) / region.originalWidth * width; + localY2 -= (region.originalHeight - region.offsetY - region.width) / region.originalHeight * height; } else { - localX += region.OffsetX / region.OriginalWidth * width; - localY += region.OffsetY / region.OriginalHeight * height; - localX2 -= (region.OriginalWidth - region.OffsetX - region.Width) / region.OriginalWidth * width; - localY2 -= (region.OriginalHeight - region.OffsetY - region.Height) / region.OriginalHeight * height; + localX += region.offsetX / region.originalWidth * width; + localY += region.offsetY / region.originalHeight * height; + localX2 -= (region.originalWidth - region.offsetX - region.width) / region.originalWidth * width; + localY2 -= (region.originalHeight - region.offsetY - region.height) / region.originalHeight * height; } } float scaleX = ScaleX; diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 46fb0dae5..f1720390f 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -47,6 +47,7 @@ namespace Spine { } public SkeletonJson (AttachmentLoader attachmentLoader) { + if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null."); this.attachmentLoader = attachmentLoader; Scale = 1; } diff --git a/spine-unity/Assets/Plugins/Spine/AtlasAsset.cs b/spine-unity/Assets/Plugins/Spine/AtlasAsset.cs index 3e938fe71..ab9ad7a39 100644 --- a/spine-unity/Assets/Plugins/Spine/AtlasAsset.cs +++ b/spine-unity/Assets/Plugins/Spine/AtlasAsset.cs @@ -22,7 +22,6 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ - using System; using System.IO; using UnityEngine; @@ -55,7 +54,7 @@ public class AtlasAsset : ScriptableObject { return atlas; try { - atlas = new Atlas(new StringReader(atlasFile.text), material, material.mainTexture.width, material.mainTexture.height); + atlas = new Atlas(new StringReader(atlasFile.text), "", new SingleTextureLoader(material)); return atlas; } catch (Exception) { Debug.LogException(new Exception("Error reading atlas file for atlas asset: " + name), this); @@ -63,3 +62,16 @@ public class AtlasAsset : ScriptableObject { } } } + +public class SingleTextureLoader : TextureLoader { + Material material; + + public SingleTextureLoader (Material material) { + this.material = material; + } + + public void Load (AtlasPage page, String path) { + page.width = material.mainTexture.width; + page.height = material.mainTexture.height; + } +} diff --git a/spine-unity/Assets/Plugins/Spine/SkeletonComponent.cs b/spine-unity/Assets/Plugins/Spine/SkeletonComponent.cs index 87ba8d1bd..551e13d72 100644 --- a/spine-unity/Assets/Plugins/Spine/SkeletonComponent.cs +++ b/spine-unity/Assets/Plugins/Spine/SkeletonComponent.cs @@ -22,7 +22,6 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ - using System; using System.IO; using System.Collections.Generic; @@ -122,12 +121,19 @@ public class SkeletonComponent : MonoBehaviour { vertices[vertexIndex + 1] = new Vector3(regionVertices[RegionAttachment.X4], regionVertices[RegionAttachment.Y4], 0); vertices[vertexIndex + 2] = new Vector3(regionVertices[RegionAttachment.X2], regionVertices[RegionAttachment.Y2], 0); vertices[vertexIndex + 3] = new Vector3(regionVertices[RegionAttachment.X3], regionVertices[RegionAttachment.Y3], 0); - - float[] regionUVs = regionAttachment.UVs; - uvs[vertexIndex] = new Vector2(regionUVs[RegionAttachment.X1], 1 - regionUVs[RegionAttachment.Y1]); - uvs[vertexIndex + 1] = new Vector2(regionUVs[RegionAttachment.X4], 1 - regionUVs[RegionAttachment.Y4]); - uvs[vertexIndex + 2] = new Vector2(regionUVs[RegionAttachment.X2], 1 - regionUVs[RegionAttachment.Y2]); - uvs[vertexIndex + 3] = new Vector2(regionUVs[RegionAttachment.X3], 1 - regionUVs[RegionAttachment.Y3]); + + AtlasRegion region = regionAttachment.Region; + if (region.rotate) { + uvs[vertexIndex + 1] = new Vector2(region.u, 1 - region.v2); + uvs[vertexIndex + 2] = new Vector2(region.u2, 1 - region.v2); + uvs[vertexIndex + 3] = new Vector2(region.u, 1 - region.v); + uvs[vertexIndex] = new Vector2(region.u2, 1 - region.v); + } else { + uvs[vertexIndex] = new Vector2(region.u, 1 - region.v2); + uvs[vertexIndex + 1] = new Vector2(region.u2, 1 - region.v2); + uvs[vertexIndex + 2] = new Vector2(region.u, 1 - region.v); + uvs[vertexIndex + 3] = new Vector2(region.u2, 1 - region.v); + } int index = quadIndex * 6; triangles[index] = vertexIndex; diff --git a/spine-unity/Assets/examples/data/spineboy.mat b/spine-unity/Assets/examples/data/spineboy.mat index 0a15059ec..fca128b10 100644 Binary files a/spine-unity/Assets/examples/data/spineboy.mat and b/spine-unity/Assets/examples/data/spineboy.mat differ diff --git a/spine-unity/Assets/examples/spineboy/spineboy.unity b/spine-unity/Assets/examples/spineboy/spineboy.unity index 5800bc11c..8305b2361 100644 Binary files a/spine-unity/Assets/examples/spineboy/spineboy.unity and b/spine-unity/Assets/examples/spineboy/spineboy.unity differ diff --git a/spine-xna/example/src/ExampleGame.cs b/spine-xna/example/src/ExampleGame.cs index 5d210c150..9d522e3f2 100644 --- a/spine-xna/example/src/ExampleGame.cs +++ b/spine-xna/example/src/ExampleGame.cs @@ -59,8 +59,7 @@ namespace Spine { protected override void LoadContent () { skeletonRenderer = new SkeletonRenderer(GraphicsDevice); - Texture2D texture = Util.LoadTexture(GraphicsDevice, "data/goblins.png"); - Atlas atlas = new Atlas("data/goblins.atlas", texture, texture.Width, texture.Height); + Atlas atlas = new Atlas("data/goblins.atlas", new XnaTextureLoader(GraphicsDevice)); SkeletonJson json = new SkeletonJson(atlas); skeleton = new Skeleton(json.ReadSkeletonData("data/goblins.json")); skeleton.SetSkin("goblingirl"); diff --git a/spine-xna/spine-xna.csproj b/spine-xna/spine-xna.csproj index 6f3cbdf02..d5f483787 100644 --- a/spine-xna/spine-xna.csproj +++ b/spine-xna/spine-xna.csproj @@ -109,6 +109,7 @@ + diff --git a/spine-xna/src/SkeletonRenderer.cs b/spine-xna/src/SkeletonRenderer.cs index 2d3587adb..c24a71432 100644 --- a/spine-xna/src/SkeletonRenderer.cs +++ b/spine-xna/src/SkeletonRenderer.cs @@ -78,12 +78,12 @@ namespace Spine { RegionAttachment regionAttachment = (RegionAttachment)attachment; SpriteBatchItem item = batcher.CreateBatchItem(); - item.Texture = (Texture2D)regionAttachment.Region.Atlas.Texture; + item.Texture = (Texture2D)regionAttachment.Region.page.texture; - byte r = (byte)(slot.R * 255); - byte g = (byte)(slot.G * 255); - byte b = (byte)(slot.B * 255); - byte a = (byte)(slot.A * 255); + byte r = (byte)(skeleton.R * slot.R * 255); + byte g = (byte)(skeleton.G * slot.G * 255); + byte b = (byte)(skeleton.B * slot.B * 255); + byte a = (byte)(skeleton.A * slot.A * 255); item.vertexTL.Color.R = r; item.vertexTL.Color.G = g; item.vertexTL.Color.B = b; @@ -116,15 +116,26 @@ namespace Spine { item.vertexTR.Position.Y = vertices[RegionAttachment.Y4]; item.vertexTR.Position.Z = 0; - float[] uvs = regionAttachment.UVs; - item.vertexTL.TextureCoordinate.X = uvs[RegionAttachment.X1]; - item.vertexTL.TextureCoordinate.Y = uvs[RegionAttachment.Y1]; - item.vertexBL.TextureCoordinate.X = uvs[RegionAttachment.X2]; - item.vertexBL.TextureCoordinate.Y = uvs[RegionAttachment.Y2]; - item.vertexBR.TextureCoordinate.X = uvs[RegionAttachment.X3]; - item.vertexBR.TextureCoordinate.Y = uvs[RegionAttachment.Y3]; - item.vertexTR.TextureCoordinate.X = uvs[RegionAttachment.X4]; - item.vertexTR.TextureCoordinate.Y = uvs[RegionAttachment.Y4]; + AtlasRegion region = regionAttachment.Region; + if (region.rotate) { + item.vertexBL.TextureCoordinate.X = region.u; + item.vertexBL.TextureCoordinate.Y = region.v2; + item.vertexBR.TextureCoordinate.X = region.u; + item.vertexBR.TextureCoordinate.Y = region.v; + item.vertexTR.TextureCoordinate.X = region.u2; + item.vertexTR.TextureCoordinate.Y = region.v; + item.vertexTL.TextureCoordinate.X = region.u2; + item.vertexTL.TextureCoordinate.Y = region.v2; + } else { + item.vertexTL.TextureCoordinate.X = region.u; + item.vertexTL.TextureCoordinate.Y = region.v2; + item.vertexBL.TextureCoordinate.X = region.u; + item.vertexBL.TextureCoordinate.Y = region.v; + item.vertexBR.TextureCoordinate.X = region.u2; + item.vertexBR.TextureCoordinate.Y = region.v; + item.vertexTR.TextureCoordinate.X = region.u2; + item.vertexTR.TextureCoordinate.Y = region.v2; + } } } } diff --git a/spine-xna/src/XnaTextureLoader.cs b/spine-xna/src/XnaTextureLoader.cs new file mode 100644 index 000000000..88657a2c4 --- /dev/null +++ b/spine-xna/src/XnaTextureLoader.cs @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. + ******************************************************************************/ + +using System; +using System.IO; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Spine { + public class XnaTextureLoader : TextureLoader { + GraphicsDevice device; + public XnaTextureLoader (GraphicsDevice device) { + this.device = device; + } + + public void Load (AtlasPage page, String path) { + Texture2D texture = Util.LoadTexture(device, path); + page.texture = texture; + page.width = texture.Width; + page.height = texture.Height; + } + } +}