mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 09:16:01 +08:00
Refactoring.
API clean up, better JSON parsing, better atlas, easier to extend.
This commit is contained in:
parent
2b49c59907
commit
3b1cf6579c
@ -60,12 +60,12 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="src\Animation.cs" />
|
||||
<Compile Include="src\Atlas.cs" />
|
||||
<Compile Include="src\Attachments\AttachmentLoader.cs" />
|
||||
<Compile Include="src\Attachments\AtlasAttachmentLoader.cs" />
|
||||
<Compile Include="src\Attachments\Attachment.cs" />
|
||||
<Compile Include="src\Attachments\AttachmentType.cs" />
|
||||
<Compile Include="src\Attachments\RegionAttachment.cs" />
|
||||
<Compile Include="src\BaseAtlas.cs" />
|
||||
<Compile Include="src\Bone.cs" />
|
||||
<Compile Include="src\BoneData.cs" />
|
||||
<Compile Include="src\Json.cs" />
|
||||
|
||||
@ -22,101 +22,133 @@
|
||||
* (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.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Spine {
|
||||
abstract public class BaseAtlas {
|
||||
List<AtlasPage> pages = new List<AtlasPage>();
|
||||
List<AtlasRegion> regions = new List<AtlasRegion>();
|
||||
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<AtlasRegion> Regions;
|
||||
public Object Texture;
|
||||
|
||||
abstract protected AtlasPage NewAtlasPage (String path);
|
||||
public Atlas (String path, Object texture, int textureWidth, int textureHeight) {
|
||||
using (Stream input = new FileStream(path, FileMode.Open, FileAccess.Read)) {
|
||||
try {
|
||||
initialize(input, texture, textureWidth, textureHeight);
|
||||
} catch (Exception ex) {
|
||||
throw new Exception("Error reading atlas file: " + path, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void load (StreamReader reader, String imagesDir) {
|
||||
public Atlas (Stream input, Object texture, int textureWidth, int textureHeight) {
|
||||
initialize(input, texture, textureWidth, textureHeight);
|
||||
}
|
||||
|
||||
private void initialize (Stream input, Object texture, int textureWidth, int textureHeight) {
|
||||
TextureWidth = textureWidth;
|
||||
TextureHeight = textureHeight;
|
||||
Texture = texture;
|
||||
|
||||
Regions = new List<AtlasRegion>();
|
||||
float invTexWidth = 1f / textureWidth;
|
||||
float invTexHeight = 1f / textureHeight;
|
||||
String[] tuple = new String[4];
|
||||
AtlasPage page = null;
|
||||
|
||||
StreamReader reader = new StreamReader(input);
|
||||
// Skip to first page entry.
|
||||
while (true) {
|
||||
String line = reader.ReadLine();
|
||||
if (line == null) break;
|
||||
if (line.Trim().Length == 0)
|
||||
page = null;
|
||||
else if (page == null) {
|
||||
page = NewAtlasPage(Path.Combine(imagesDir, line));
|
||||
break;
|
||||
}
|
||||
reader.ReadLine(); // Skip first page name.
|
||||
|
||||
page.Format = (Format)Enum.Parse(typeof(Format), readValue(reader), false);
|
||||
Format = (Format)Enum.Parse(typeof(Format), readValue(reader), false);
|
||||
|
||||
readTuple(reader, tuple);
|
||||
page.MinFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0]);
|
||||
page.MagFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1]);
|
||||
readTuple(reader, tuple);
|
||||
MinFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0]);
|
||||
MagFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1]);
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
pages.Add(page);
|
||||
while (true) {
|
||||
String line = reader.ReadLine();
|
||||
if (line == null || line.Trim().Length == 0) break;
|
||||
|
||||
} else {
|
||||
AtlasRegion region = new AtlasRegion();
|
||||
region.Name = line;
|
||||
region.Page = page;
|
||||
AtlasRegion region = new AtlasRegion();
|
||||
region.Atlas = this;
|
||||
region.Name = line;
|
||||
|
||||
region.Rotate = Boolean.Parse(readValue(reader));
|
||||
region.Rotate = Boolean.Parse(readValue(reader));
|
||||
|
||||
readTuple(reader, tuple);
|
||||
int x = int.Parse(tuple[0]);
|
||||
int y = int.Parse(tuple[1]);
|
||||
readTuple(reader, tuple);
|
||||
int x = int.Parse(tuple[0]);
|
||||
int y = int.Parse(tuple[1]);
|
||||
|
||||
readTuple(reader, tuple);
|
||||
int width = int.Parse(tuple[0]);
|
||||
int height = int.Parse(tuple[1]);
|
||||
readTuple(reader, tuple);
|
||||
int width = int.Parse(tuple[0]);
|
||||
int height = int.Parse(tuple[1]);
|
||||
|
||||
float invTexWidth = 1f / page.GetTextureWidth();
|
||||
float invTexHeight = 1f / page.GetTextureHeight();
|
||||
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);
|
||||
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);
|
||||
|
||||
if (readTuple(reader, tuple) == 4) { // split is optional
|
||||
region.Splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
|
||||
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 (StreamReader 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();
|
||||
}
|
||||
|
||||
@ -124,12 +156,14 @@ namespace Spine {
|
||||
static int readTuple (StreamReader 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++) {
|
||||
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();
|
||||
@ -143,8 +177,9 @@ 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;
|
||||
}
|
||||
}
|
||||
@ -175,19 +210,8 @@ namespace Spine {
|
||||
Repeat
|
||||
}
|
||||
|
||||
abstract public class AtlasPage {
|
||||
public Format Format;
|
||||
public TextureFilter MinFilter;
|
||||
public TextureFilter MagFilter;
|
||||
public TextureWrap UWrap;
|
||||
public TextureWrap VWrap;
|
||||
|
||||
abstract public int GetTextureWidth ();
|
||||
abstract public int GetTextureHeight ();
|
||||
}
|
||||
|
||||
public class AtlasRegion {
|
||||
public AtlasPage Page;
|
||||
public Atlas Atlas;
|
||||
public float U, V;
|
||||
public float U2, V2;
|
||||
public int Width, Height;
|
||||
@ -27,9 +27,9 @@ using System;
|
||||
|
||||
namespace Spine {
|
||||
public class AtlasAttachmentLoader : AttachmentLoader {
|
||||
private BaseAtlas atlas;
|
||||
private Atlas atlas;
|
||||
|
||||
public AtlasAttachmentLoader (BaseAtlas atlas) {
|
||||
public AtlasAttachmentLoader (Atlas atlas) {
|
||||
if (atlas == null) throw new ArgumentNullException("atlas cannot be null.");
|
||||
this.atlas = atlas;
|
||||
}
|
||||
|
||||
@ -96,16 +96,18 @@ namespace Spine {
|
||||
float localX = -localX2;
|
||||
float localY = -localY2;
|
||||
AtlasRegion region = Region;
|
||||
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;
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
float scaleX = ScaleX;
|
||||
float scaleY = ScaleY;
|
||||
|
||||
@ -81,12 +81,10 @@ namespace Spine
|
||||
/// </summary>
|
||||
/// <param name="json">A JSON string.</param>
|
||||
/// <returns>An List<object>, a Dictionary<string, object>, a float, an integer,a string, null, true, or false</returns>
|
||||
public static object Deserialize(string json) {
|
||||
// save the string for debug information
|
||||
public static object Deserialize (TextReader json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
return Parser.Parse(json);
|
||||
}
|
||||
|
||||
@ -109,14 +107,14 @@ namespace Spine
|
||||
NULL
|
||||
};
|
||||
|
||||
StringReader json;
|
||||
TextReader json;
|
||||
|
||||
Parser(string jsonString) {
|
||||
json = new StringReader(jsonString);
|
||||
Parser(TextReader reader) {
|
||||
json = reader;
|
||||
}
|
||||
|
||||
public static object Parse(string jsonString) {
|
||||
using (var instance = new Parser(jsonString)) {
|
||||
|
||||
public static object Parse (TextReader reader) {
|
||||
using (var instance = new Parser(reader)) {
|
||||
return instance.ParseValue();
|
||||
}
|
||||
}
|
||||
@ -288,14 +286,6 @@ namespace Spine
|
||||
|
||||
object ParseNumber() {
|
||||
string number = NextWord;
|
||||
|
||||
//NO!! always serialize to a float
|
||||
//if (number.IndexOf('.') == -1) {
|
||||
// long parsedInt;
|
||||
// Int64.TryParse(number, out parsedInt);
|
||||
// return parsedInt;
|
||||
//}
|
||||
|
||||
float parsedFloat;
|
||||
float.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out parsedFloat);
|
||||
return parsedFloat;
|
||||
|
||||
@ -41,7 +41,7 @@ namespace Spine {
|
||||
private AttachmentLoader attachmentLoader;
|
||||
public float Scale { get; set; }
|
||||
|
||||
public SkeletonJson (BaseAtlas atlas) {
|
||||
public SkeletonJson (Atlas atlas) {
|
||||
this.attachmentLoader = new AtlasAttachmentLoader(atlas);
|
||||
Scale = 1;
|
||||
}
|
||||
@ -51,14 +51,21 @@ namespace Spine {
|
||||
Scale = 1;
|
||||
}
|
||||
|
||||
public SkeletonData readSkeletonData (String name, String json) {
|
||||
if (json == null)
|
||||
public SkeletonData ReadSkeletonData (String path) {
|
||||
using (Stream input = new FileStream(path, FileMode.Open, FileAccess.Read)) {
|
||||
SkeletonData skeletonData = ReadSkeletonData(input);
|
||||
skeletonData.Name = Path.GetFileNameWithoutExtension(path);
|
||||
return skeletonData;
|
||||
}
|
||||
}
|
||||
|
||||
public SkeletonData ReadSkeletonData (Stream input) {
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("json cannot be null.");
|
||||
|
||||
SkeletonData skeletonData = new SkeletonData();
|
||||
skeletonData.Name = name;
|
||||
|
||||
var root = Json.Deserialize(json) as Dictionary<String, Object>;
|
||||
var root = Json.Deserialize(new StreamReader(input)) as Dictionary<String, Object>;
|
||||
|
||||
// Bones.
|
||||
foreach (Dictionary<String, Object> boneMap in (List<Object>)root["bones"]) {
|
||||
|
||||
@ -58,11 +58,13 @@ namespace Spine {
|
||||
|
||||
protected override void LoadContent () {
|
||||
skeletonRenderer = new SkeletonRenderer(GraphicsDevice);
|
||||
Atlas atlas = new Atlas(GraphicsDevice, "data/goblins.atlas");
|
||||
|
||||
Texture2D texture = Util.LoadTexture(GraphicsDevice, "data/goblins.png");
|
||||
Atlas atlas = new Atlas("data/goblins.atlas", texture, texture.Width, texture.Height);
|
||||
SkeletonJson json = new SkeletonJson(atlas);
|
||||
skeleton = new Skeleton(json.readSkeletonData("goblins", File.ReadAllText("data/goblins.json")));
|
||||
skeleton = new Skeleton(json.ReadSkeletonData("data/goblins.json"));
|
||||
skeleton.SetSkin("goblingirl");
|
||||
skeleton.SetSlotsToBindPose();
|
||||
skeleton.SetSlotsToBindPose(); // Without this the skin attachments won't be attached. See SetSkin.
|
||||
animation = skeleton.Data.FindAnimation("walk");
|
||||
|
||||
skeleton.RootBone.X = 320;
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="src\Atlas.cs" />
|
||||
<Compile Include="src\Util.cs" />
|
||||
<Compile Include="src\SkeletonRenderer.cs" />
|
||||
<Compile Include="src\SpriteBatcher.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -1,109 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* 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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using Microsoft.Xna.Framework.GamerServices;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Media;
|
||||
|
||||
namespace Spine {
|
||||
public class Atlas : BaseAtlas {
|
||||
private GraphicsDevice device;
|
||||
|
||||
public Atlas (GraphicsDevice device, String atlasFile) {
|
||||
this.device = device;
|
||||
using (StreamReader reader = new StreamReader(atlasFile)) {
|
||||
load(reader, Path.GetDirectoryName(atlasFile));
|
||||
}
|
||||
}
|
||||
|
||||
override protected AtlasPage NewAtlasPage (String path) {
|
||||
XnaAtlasPage page = new XnaAtlasPage();
|
||||
page.Texture = loadTexture(path);
|
||||
return page;
|
||||
}
|
||||
|
||||
private Texture2D loadTexture (string path) {
|
||||
Texture2D file;
|
||||
using (Stream fileStream = new FileStream(path, FileMode.Open)) {
|
||||
file = Texture2D.FromStream(device, fileStream);
|
||||
}
|
||||
|
||||
// Setup a render target to hold our final texture which will have premulitplied alpha values
|
||||
RenderTarget2D result = new RenderTarget2D(device, file.Width, file.Height);
|
||||
device.SetRenderTarget(result);
|
||||
device.Clear(Color.Black);
|
||||
|
||||
// Multiply each color by the source alpha, and write in just the color values into the final texture
|
||||
BlendState blendColor = new BlendState();
|
||||
blendColor.ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue;
|
||||
blendColor.AlphaDestinationBlend = Blend.Zero;
|
||||
blendColor.ColorDestinationBlend = Blend.Zero;
|
||||
blendColor.AlphaSourceBlend = Blend.SourceAlpha;
|
||||
blendColor.ColorSourceBlend = Blend.SourceAlpha;
|
||||
|
||||
SpriteBatch spriteBatch = new SpriteBatch(device);
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, blendColor);
|
||||
spriteBatch.Draw(file, file.Bounds, Color.White);
|
||||
spriteBatch.End();
|
||||
|
||||
// Now copy over the alpha values from the PNG source texture to the final one, without multiplying them
|
||||
BlendState blendAlpha = new BlendState();
|
||||
blendAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;
|
||||
blendAlpha.AlphaDestinationBlend = Blend.Zero;
|
||||
blendAlpha.ColorDestinationBlend = Blend.Zero;
|
||||
blendAlpha.AlphaSourceBlend = Blend.One;
|
||||
blendAlpha.ColorSourceBlend = Blend.One;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha);
|
||||
spriteBatch.Draw(file, file.Bounds, Color.White);
|
||||
spriteBatch.End();
|
||||
|
||||
// Release the GPU back to drawing to the screen
|
||||
device.SetRenderTarget(null);
|
||||
|
||||
return result as Texture2D;
|
||||
}
|
||||
}
|
||||
|
||||
public class XnaAtlasPage : AtlasPage {
|
||||
public Texture2D Texture { get; set; }
|
||||
|
||||
override public int GetTextureWidth () {
|
||||
return Texture.Width;
|
||||
}
|
||||
|
||||
override public int GetTextureHeight () {
|
||||
return Texture.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,6 +34,7 @@ namespace Spine {
|
||||
SpriteBatcher batcher;
|
||||
BasicEffect effect;
|
||||
RasterizerState rasterizerState;
|
||||
public BlendState BlendState { get; set; }
|
||||
|
||||
public SkeletonRenderer (GraphicsDevice device) {
|
||||
this.device = device;
|
||||
@ -49,12 +50,14 @@ namespace Spine {
|
||||
rasterizerState = new RasterizerState();
|
||||
rasterizerState.CullMode = CullMode.None;
|
||||
|
||||
BlendState = BlendState.AlphaBlend;
|
||||
|
||||
Bone.yDown = true;
|
||||
}
|
||||
|
||||
public void Begin () {
|
||||
device.RasterizerState = rasterizerState;
|
||||
device.BlendState = BlendState.AlphaBlend;
|
||||
device.BlendState = BlendState;
|
||||
|
||||
effect.Projection = Matrix.CreateOrthographicOffCenter(0, device.Viewport.Width, device.Viewport.Height, 0, 1, 0);
|
||||
}
|
||||
@ -71,13 +74,11 @@ namespace Spine {
|
||||
for (int i = 0, n = drawOrder.Count; i < n; i++) {
|
||||
Slot slot = drawOrder[i];
|
||||
Attachment attachment = slot.Attachment;
|
||||
if (attachment == null)
|
||||
continue;
|
||||
if (attachment is RegionAttachment) {
|
||||
RegionAttachment regionAttachment = (RegionAttachment)attachment;
|
||||
|
||||
SpriteBatchItem item = batcher.CreateBatchItem();
|
||||
item.Texture = ((XnaAtlasPage)regionAttachment.Region.Page).Texture;
|
||||
item.Texture = (Texture2D)regionAttachment.Region.Atlas.Texture;
|
||||
|
||||
byte r = (byte)(slot.R * 255);
|
||||
byte g = (byte)(slot.G * 255);
|
||||
|
||||
57
spine-xna/src/Util.cs
Normal file
57
spine-xna/src/Util.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Spine {
|
||||
static public class Util {
|
||||
static public Texture2D LoadTexture (GraphicsDevice device, String path) {
|
||||
using (Stream input = new FileStream(path, FileMode.Open, FileAccess.Read)) {
|
||||
try {
|
||||
return Util.LoadTexture(device, input);
|
||||
} catch (Exception ex) {
|
||||
throw new Exception("Error reading texture file: " + path, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public Texture2D LoadTexture (GraphicsDevice device, Stream input) {
|
||||
Texture2D file = Texture2D.FromStream(device, input);
|
||||
|
||||
// Setup a render target to hold our final texture which will have premulitplied alpha values
|
||||
RenderTarget2D result = new RenderTarget2D(device, file.Width, file.Height);
|
||||
device.SetRenderTarget(result);
|
||||
device.Clear(Color.Black);
|
||||
|
||||
// Multiply each color by the source alpha, and write in just the color values into the final texture
|
||||
BlendState blendColor = new BlendState();
|
||||
blendColor.ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue;
|
||||
blendColor.AlphaDestinationBlend = Blend.Zero;
|
||||
blendColor.ColorDestinationBlend = Blend.Zero;
|
||||
blendColor.AlphaSourceBlend = Blend.SourceAlpha;
|
||||
blendColor.ColorSourceBlend = Blend.SourceAlpha;
|
||||
|
||||
SpriteBatch spriteBatch = new SpriteBatch(device);
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, blendColor);
|
||||
spriteBatch.Draw(file, file.Bounds, Color.White);
|
||||
spriteBatch.End();
|
||||
|
||||
// Now copy over the alpha values from the PNG source texture to the final one, without multiplying them
|
||||
BlendState blendAlpha = new BlendState();
|
||||
blendAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;
|
||||
blendAlpha.AlphaDestinationBlend = Blend.Zero;
|
||||
blendAlpha.ColorDestinationBlend = Blend.Zero;
|
||||
blendAlpha.AlphaSourceBlend = Blend.One;
|
||||
blendAlpha.ColorSourceBlend = Blend.One;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha);
|
||||
spriteBatch.Draw(file, file.Bounds, Color.White);
|
||||
spriteBatch.End();
|
||||
|
||||
// Release the GPU back to drawing to the screen
|
||||
device.SetRenderTarget(null);
|
||||
|
||||
return result as Texture2D;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user