Переглянути джерело

[xna] Added two color tinting, support for arbitrary Effects to be set on SkeletonRenderer

badlogic 8 роки тому
батько
коміт
8fd8cfbf46

+ 12 - 1
CHANGELOG.md

@@ -104,7 +104,18 @@
      * We will continue to bundle the unitypackage with the empty .cs files of deprecated classes until Spine 3.7 to ensure the upgrade process does not break.
    * The [SpineAttachment(slotField:)] optional parameter found property value now acts as a Find(slotName) argument rather than Contains(slotName).
    * `SkeletonAnimator` now has autoreset set to true by default. Old prefabs and scene values will have been serialized to whatever value it was previously. This change only applies to new instances of SkeletonAnimator.
-   * Warnings and conditionals checking for specific Unity 5.2-and-below incompatibility have been removed. 
+   * Warnings and conditionals checking for specific Unity 5.2-and-below incompatibility have been removed.
+
+## XNA
+ * Added support for clipping
+ * Removed `RegionBatcher` and `SkeletonRegionRenderer`, renamed `SkeletonMeshRenderer` to `SkeletonRenderer`
+ * Added support for two color tint. For it to work, you need to add the `SpineEffect.fx` file to your content project, then load it via `var effect = Content.Load<Effect>("SpineEffect");`, and set it on the `SkeletonRenderer`. See the example project for code.
+ * Added support for any `Effect` to be used by `SkeletonRenderer`
+
+ ## MonoGame
+ * Added support for clipping
+ * Removed `RegionBatcher` and `SkeletonRegionRenderer`, renamed `SkeletonMeshRenderer` to `SkeletonRenderer`
+ *
 
 ## Java
  * **Breaking changes**

+ 1 - 1
spine-csharp/src/Slot.cs

@@ -52,7 +52,7 @@ namespace Spine {
 		public float R2 { get { return r2; } set { r2 = value; } }
 		public float G2 { get { return g2; } set { g2 = value; } }
 		public float B2 { get { return b2; } set { b2 = value; } }
-		public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } }
+		public bool HasSecondColor { get { return data.hasSecondColor; } set { data.hasSecondColor = value; } }
 
 		/// <summary>May be null.</summary>
 		public Attachment Attachment {

+ 59 - 0
spine-xna/example-content/SpineEffect.fx

@@ -0,0 +1,59 @@
+float4x4 World;
+float4x4 View;
+float4x4 Projection;
+
+sampler TextureSampler : register(s0);
+
+// TODO: add effect parameters here.
+
+struct VertexShaderInput
+{
+    float4 Position : POSITION0;
+	float4 Color : COLOR0;
+	float4 TextureCoordinate : TEXCOORD0;
+	float4 Color2 : COLOR1;
+};
+
+struct VertexShaderOutput
+{
+    float4 Position : POSITION0;
+	float4 Color : COLOR0;
+	float4 TextureCoordinate : TEXCOORD0;
+	float4 Color2 : COLOR1;
+};
+
+VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
+{
+    VertexShaderOutput output;
+
+    float4 worldPosition = mul(input.Position, World);
+    float4 viewPosition = mul(worldPosition, View);
+    output.Position = mul(viewPosition, Projection);
+	output.TextureCoordinate = input.TextureCoordinate;
+	output.Color = input.Color;
+	output.Color2 = input.Color2;
+
+    return output;
+}
+
+float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
+{
+	float4 texColor = tex2D(TextureSampler, input.TextureCoordinate);
+	float alpha = texColor.a * input.Color.a;
+	float4 output;
+	output.a = alpha;
+	output.rgb = (1.0 - texColor.rgb) * input.Color2.rgb * alpha + texColor.rgb * input.Color.rgb;
+
+	return output;
+}
+
+technique Technique1
+{
+    pass Pass1
+    {
+        // TODO: set renderstates here.
+
+        VertexShader = compile vs_2_0 VertexShaderFunction();
+        PixelShader = compile ps_2_0 PixelShaderFunction();
+    }
+}

+ 37 - 0
spine-xna/example-content/spine-xna-example-content.contentproj

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+  <PropertyGroup>
+    <ProjectGuid>{D0632D20-BC92-4A73-896B-30BB687732B8}</ProjectGuid>
+    <ProjectTypeGuids>{96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
+    <OutputPath>bin\$(Platform)\$(Configuration)</OutputPath>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.EffectImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.FBXImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.TextureImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.XImporter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.AudioImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
+    <Reference Include="Microsoft.Xna.Framework.Content.Pipeline.VideoImporters, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="SpineEffect.fx">
+      <Name>SpineEffect</Name>
+      <Importer>EffectImporter</Importer>
+      <Processor>EffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 13 - 0
spine-xna/example/data/TwoColorTest.atlas

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

+ 150 - 0
spine-xna/example/data/TwoColorTest.json

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

BIN
spine-xna/example/data/TwoColorTest.png


+ 22 - 8
spine-xna/example/spine-xna-example.csproj

@@ -2,7 +2,7 @@
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
     <ProjectGuid>{29CC4385-294A-4885-A3E8-FD4825E0CFDD}</ProjectGuid>
-    <ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <ProjectTypeGuids>{6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
     <OutputType>WinExe</OutputType>
@@ -134,6 +134,9 @@
     <Content Include="data\tank.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="data\TwoColorTest.png">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
     <Content Include="Game.ico" />
     <Content Include="GameThumbnail.png" />
     <None Include="data\tank.atlas">
@@ -145,12 +148,23 @@
     <None Include="data\tank.skel">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
+    <None Include="data\TwoColorTest.atlas">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Include="data\TwoColorTest.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\spine-csharp\spine-csharp.csproj">
       <Project>{94144e22-2431-4a8f-ac04-dec22f7edd8f}</Project>
       <Name>spine-csharp</Name>
     </ProjectReference>
+    <ProjectReference Include="..\example-content\spine-xna-example-content.contentproj">
+      <Project>{D0632D20-BC92-4A73-896B-30BB687732B8}</Project>
+      <Name>spine-xna-example-content</Name>
+      <XnaReferenceType>Content</XnaReferenceType>
+    </ProjectReference>
     <ProjectReference Include="..\spine-xna.csproj">
       <Project>{7F8F2327-C016-49C8-BB4D-F3F77971961E}</Project>
       <Name>spine-xna</Name>
@@ -214,12 +228,12 @@
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
-  <!--
-      To modify your build process, add your task inside one of the targets below and uncomment it. 
-      Other similar extension points exist, see Microsoft.Common.targets.
-      <Target Name="BeforeBuild">
-      </Target>
-      <Target Name="AfterBuild">
-      </Target>
+  <!--
+      To modify your build process, add your task inside one of the targets below and uncomment it. 
+      Other similar extension points exist, see Microsoft.Common.targets.
+      <Target Name="BeforeBuild">
+      </Target>
+      <Target Name="AfterBuild">
+      </Target>
     -->
 </Project>

+ 23 - 7
spine-xna/example/src/ExampleGame.cs

@@ -42,7 +42,7 @@ using Spine;
 namespace Spine {
 	public class Example : Microsoft.Xna.Framework.Game {
 		GraphicsDeviceManager graphics;
-		SkeletonMeshRenderer skeletonRenderer;
+		SkeletonRenderer skeletonRenderer;
 		Skeleton skeleton;
 		Slot headSlot;
 		AnimationState state;
@@ -70,23 +70,30 @@ namespace Spine {
 		}
 
 		protected override void LoadContent () {
-			skeletonRenderer = new SkeletonMeshRenderer(GraphicsDevice);
+			var spineEffect = Content.Load<Effect>("spine-xna-example-content\\SpineEffect");
+			spineEffect.Parameters["World"].SetValue(Matrix.Identity);
+			spineEffect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up));			
+
+			skeletonRenderer = new SkeletonRenderer(GraphicsDevice);
 			skeletonRenderer.PremultipliedAlpha = false;
+			skeletonRenderer.Effect = spineEffect;
 
-			String name = "spineboy";
+			// String name = "spineboy";
 			// String name = "goblins-mesh";
 			// String name = "raptor";
 			// String name = "tank";
 			// String name = "coin";
+			String name = "TwoColorTest";
 			bool binaryData = false;
 
-			Atlas atlas = new Atlas(assetsFolder + name + ".atlas", new XnaTextureLoader(GraphicsDevice));
+			Atlas atlas = new Atlas(assetsFolder + name + ".atlas", new XnaTextureLoader(GraphicsDevice));			
 
 			float scale = 1;
 			if (name == "spineboy") scale = 0.6f;
 			if (name == "raptor") scale = 0.5f;
 			if (name == "tank") scale = 0.3f;
-			if (name == "coin") scale = 1;	
+			if (name == "coin") scale = 1;
+			if (name == "TwoColorTest") scale = 0.5f;
 
 			SkeletonData skeletonData;
 			if (binaryData) {
@@ -129,12 +136,16 @@ namespace Spine {
 			}
 			else if (name == "tank") {
 				state.SetAnimation(0, "drive", true);
-			} else {
+			}
+			else if (name == "TwoColorTest") {
+				state.SetAnimation(0, "animation", true);
+			}
+			else {
 				state.SetAnimation(0, "walk", true);
 			}
 
 			skeleton.X = 400 + (name == "tank" ? 300: 0);
-			skeleton.Y = 580;
+			skeleton.Y = 580 + (name == "TwoColorTest" ? -400 : 0);
 			skeleton.UpdateWorldTransform();
 
 			headSlot = skeleton.FindSlot("head");
@@ -159,6 +170,11 @@ namespace Spine {
 			state.Update(gameTime.ElapsedGameTime.Milliseconds / 1000f);
 			state.Apply(skeleton);			
 			skeleton.UpdateWorldTransform();
+			if (skeletonRenderer.Effect is BasicEffect) {
+				((BasicEffect)skeletonRenderer.Effect).Projection = Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 1, 0);
+			} else {
+				skeletonRenderer.Effect.Parameters["Projection"].SetValue(Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 1, 0));
+			}			
 			skeletonRenderer.Begin();
 			skeletonRenderer.Draw(skeleton);
 			skeletonRenderer.End();

+ 1 - 3
spine-xna/spine-xna.csproj

@@ -110,10 +110,8 @@
   <ItemGroup>
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="src\MeshBatcher.cs" />
-    <Compile Include="src\RegionBatcher.cs" />
     <Compile Include="src\XnaTextureLoader.cs" />
     <Compile Include="src\Util.cs" />
-    <Compile Include="src\SkeletonRegionRenderer.cs" />
   </ItemGroup>
   <ItemGroup>
     <BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
@@ -138,7 +136,7 @@
     </BootstrapperPackage>
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="src\SkeletonMeshRenderer.cs" />
+    <Compile Include="src\SkeletonRenderer.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\spine-csharp\spine-csharp.csproj">

+ 13 - 1
spine-xna/spine-xna.sln

@@ -1,6 +1,8 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Express 2012 for Windows Desktop
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna", "spine-xna.csproj", "{7F8F2327-C016-49C8-BB4D-F3F77971961E}"
 	ProjectSection(ProjectDependencies) = postProject
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F} = {94144E22-2431-4A8F-AC04-DEC22F7EDD8F}
@@ -10,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna-example", "exampl
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-csharp", "..\spine-csharp\spine-csharp.csproj", "{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna-example-content", "example-content\spine-xna-example-content.contentproj", "{D0632D20-BC92-4A73-896B-30BB687732B8}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -45,11 +49,19 @@ Global
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|x86.Build.0 = Debug|Any CPU
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Any CPU.Build.0 = Release|Any CPU
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
 		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|x86.ActiveCfg = Release|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|x86.Build.0 = Release|Any CPU
+		{D0632D20-BC92-4A73-896B-30BB687732B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D0632D20-BC92-4A73-896B-30BB687732B8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{D0632D20-BC92-4A73-896B-30BB687732B8}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{D0632D20-BC92-4A73-896B-30BB687732B8}.Release|Any CPU.ActiveCfg = Debug|Any CPU
+		{D0632D20-BC92-4A73-896B-30BB687732B8}.Release|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{D0632D20-BC92-4A73-896B-30BB687732B8}.Release|x86.ActiveCfg = Debug|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 24 - 5
spine-xna/src/MeshBatcher.cs

@@ -35,6 +35,25 @@ using Microsoft.Xna.Framework.Graphics;
 using Microsoft.Xna.Framework;
 
 namespace Spine {
+	public struct VertexPositionColorTextureColor : IVertexType {
+		public Vector3 Position;
+		public Color Color;
+		public Vector2 TextureCoordinate;
+		public Color Color2;
+
+		public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
+		(
+			new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
+			new VertexElement(12, VertexElementFormat.Color, VertexElementUsage.Color, 0),
+			new VertexElement(16, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
+			new VertexElement(24, VertexElementFormat.Color, VertexElementUsage.Color, 1)
+		);
+
+		VertexDeclaration IVertexType.VertexDeclaration {
+			get { return VertexDeclaration; }
+		}
+	}
+
 	// #region License
 	// /*
 	// Microsoft Public License (Ms-PL)
@@ -80,7 +99,7 @@ namespace Spine {
 	public class MeshBatcher {
 		private readonly List<MeshItem> items;
 		private readonly Queue<MeshItem> freeItems;
-		private VertexPositionColorTexture[] vertexArray = { };
+		private VertexPositionColorTextureColor[] vertexArray = { };
 		private short[] triangles = { };
 
 		public MeshBatcher () {
@@ -92,7 +111,7 @@ namespace Spine {
 		/// <summary>Returns a pooled MeshItem.</summary>
 		public MeshItem NextItem (int vertexCount, int triangleCount) {
 			MeshItem item = freeItems.Count > 0 ? freeItems.Dequeue() : new MeshItem();
-			if (item.vertices.Length < vertexCount) item.vertices = new VertexPositionColorTexture[vertexCount];
+			if (item.vertices.Length < vertexCount) item.vertices = new VertexPositionColorTextureColor[vertexCount];
 			if (item.triangles.Length < triangleCount) item.triangles = new int[triangleCount];
 			item.vertexCount = vertexCount;
 			item.triangleCount = triangleCount;
@@ -101,7 +120,7 @@ namespace Spine {
 		}
 
 		private void EnsureCapacity (int vertexCount, int triangleCount) {
-			if (vertexArray.Length < vertexCount) vertexArray = new VertexPositionColorTexture[vertexCount];
+			if (vertexArray.Length < vertexCount) vertexArray = new VertexPositionColorTextureColor[vertexCount];
 			if (triangles.Length < triangleCount) triangles = new short[triangleCount];
 		}
 
@@ -154,14 +173,14 @@ namespace Spine {
 				PrimitiveType.TriangleList,
 				vertexArray, 0, vertexCount,
 				triangles, 0, triangleCount / 3,
-				VertexPositionColorTexture.VertexDeclaration);
+				VertexPositionColorTextureColor.VertexDeclaration);
 		}
 	}
 
 	public class MeshItem {
 		public Texture2D texture;
 		public int vertexCount, triangleCount;
-		public VertexPositionColorTexture[] vertices = { };
+		public VertexPositionColorTextureColor[] vertices = { };
 		public int[] triangles = { };
 	}
 }

+ 0 - 182
spine-xna/src/RegionBatcher.cs

@@ -1,182 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License
- * Version 2.3
- * 
- * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the
- * Software or develop new applications using the Software 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; 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.Collections.Generic;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework;
-
-namespace Spine {
-	// #region License
-	// /*
-	// Microsoft Public License (Ms-PL)
-	// MonoGame - Copyright © 2009 The MonoGame Team
-	//
-	// All rights reserved.
-	//
-	// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
-	// accept the license, do not use the software.
-	//
-	// 1. Definitions
-	// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
-	// U.S. copyright law.
-	//
-	// A "contribution" is the original software, or any additions or changes to the software.
-	// A "contributor" is any person that distributes its contribution under this license.
-	// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
-	//
-	// 2. Grant of Rights
-	// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-	// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
-	// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
-	// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
-	//
-	// 3. Conditions and Limitations
-	// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
-	// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
-	// your patent license from such contributor to the software ends automatically.
-	// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
-	// notices that are present in the software.
-	// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including
-	// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
-	// code form, you may only do so under a license that complies with this license.
-	// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
-	// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
-	// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
-	// purpose and non-infringement.
-	// */
-	// #endregion License
-	//
-
-	/// <summary>Draws batched quads using indices.</summary>
-	public class RegionBatcher {
-		private const int maxBatchSize = short.MaxValue / 6; // 6 = 4 vertices unique and 2 shared, per quad
-		private readonly List<RegionItem> items;
-		private readonly Queue<RegionItem> freeItems;
-		private VertexPositionColorTexture[] vertexArray;
-		private short[] indices;
-
-		public RegionBatcher () {
-			items = new List<RegionItem>(256);
-			freeItems = new Queue<RegionItem>(256);
-			EnsureArrayCapacity(256);
-		}
-
-		/// <summary>Returns a pooled RegionItem.</summary>
-		public RegionItem NextItem () {
-			RegionItem item = freeItems.Count > 0 ? freeItems.Dequeue() : new RegionItem();
-			items.Add(item);
-			return item;
-		}
-
-		/// <summary>Resize and recreate the indices and vertex position color buffers.</summary>
-		private void EnsureArrayCapacity (int itemCount) {
-			if (indices != null && indices.Length >= 6 * itemCount) return;
-
-			short[] newIndices = new short[6 * itemCount];
-			int start = 0;
-			if (indices != null) {
-				indices.CopyTo(newIndices, 0);
-				start = indices.Length / 6;
-			}
-			for (var i = start; i < itemCount; i++) {
-				/* TL    TR
-				 *  0----1 0,1,2,3 = index offsets for vertex indices
-				 *  |    | TL,TR,BL,BR are vertex references in RegionItem.
-				 *  2----3
-				 * BL    BR */
-				newIndices[i * 6 + 0] = (short)(i * 4);
-				newIndices[i * 6 + 1] = (short)(i * 4 + 1);
-				newIndices[i * 6 + 2] = (short)(i * 4 + 2);
-				newIndices[i * 6 + 3] = (short)(i * 4 + 1);
-				newIndices[i * 6 + 4] = (short)(i * 4 + 3);
-				newIndices[i * 6 + 5] = (short)(i * 4 + 2);
-			}
-			indices = newIndices;
-
-			vertexArray = new VertexPositionColorTexture[4 * itemCount];
-		}
-
-		public void Draw (GraphicsDevice device) {
-			if (items.Count == 0) return;
-
-			int itemIndex = 0;
-			int itemCount = items.Count;
-			while (itemCount > 0) {
-				int itemsToProcess = Math.Min(itemCount, maxBatchSize);
-				EnsureArrayCapacity(itemsToProcess);
-
-				var count = 0;
-				Texture2D texture = null;
-				for (int i = 0; i < itemsToProcess; i++, itemIndex++) {
-					RegionItem item = items[itemIndex];
-					if (item.texture != texture) {
-						FlushVertexArray(device, count);
-						texture = item.texture;
-						count = 0;
-						device.Textures[0] = texture;
-					}
-
-					vertexArray[count++] = item.vertexTL;
-					vertexArray[count++] = item.vertexTR;
-					vertexArray[count++] = item.vertexBL;
-					vertexArray[count++] = item.vertexBR;
-
-					item.texture = null;
-					freeItems.Enqueue(item);
-				}
-				FlushVertexArray(device, count);
-				itemCount -= itemsToProcess;
-			}
-			items.Clear();
-		}
-
-		/// <summary>Sends the triangle list to the graphics device.</summary>
-		/// <param name="start">Start index of vertices to draw. Not used except to compute the count of vertices to draw.</param>
-		/// <param name="end">End index of vertices to draw. Not used except to compute the count of vertices to draw.</param>
-		private void FlushVertexArray (GraphicsDevice device, int count) {
-			if (count == 0) return;
-			device.DrawUserIndexedPrimitives(
-				PrimitiveType.TriangleList,
-				vertexArray, 0, count,
-				indices, 0, (count / 4) * 2,
-				VertexPositionColorTexture.VertexDeclaration);
-		}
-	}
-
-	public class RegionItem {
-		public Texture2D texture;
-		public VertexPositionColorTexture vertexTL;
-		public VertexPositionColorTexture vertexTR;
-		public VertexPositionColorTexture vertexBL;
-		public VertexPositionColorTexture vertexBR;
-	}
-}

+ 0 - 143
spine-xna/src/SkeletonRegionRenderer.cs

@@ -1,143 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License
- * Version 2.3
- * 
- * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the
- * Software or develop new applications using the Software 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; 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.Collections.Generic;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework;
-
-namespace Spine {
-	/// <summary>Draws region attachments.</summary>
-	public class SkeletonRegionRenderer {
-		GraphicsDevice device;
-		RegionBatcher batcher;
-		RasterizerState rasterizerState;
-		float[] vertices = new float[8];
-		BlendState defaultBlendState;
-
-		BasicEffect effect;
-		public BasicEffect Effect { get { return effect; } set { effect = value; } }
-
-		private bool premultipliedAlpha;
-		public bool PremultipliedAlpha { get { return premultipliedAlpha; } set { premultipliedAlpha = value; } }
-
-		public SkeletonRegionRenderer (GraphicsDevice device) {
-			this.device = device;
-
-			batcher = new RegionBatcher();
-
-			effect = new BasicEffect(device);
-			effect.World = Matrix.Identity;
-			effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
-			effect.TextureEnabled = true;
-			effect.VertexColorEnabled = true;
-
-			rasterizerState = new RasterizerState();
-			rasterizerState.CullMode = CullMode.None;
-
-			Bone.yDown = true;
-		}
-
-		public void Begin () {
-			defaultBlendState = premultipliedAlpha ? BlendState.AlphaBlend : BlendState.NonPremultiplied;
-
-			device.RasterizerState = rasterizerState;
-			device.BlendState = defaultBlendState;
-
-			effect.Projection = Matrix.CreateOrthographicOffCenter(0, device.Viewport.Width, device.Viewport.Height, 0, 1, 0);
-		}
-
-		public void End () {
-			foreach (EffectPass pass in effect.CurrentTechnique.Passes) {
-				pass.Apply();
-				batcher.Draw(device);
-			}
-		}
-
-		public void Draw (Skeleton skeleton) {
-			var drawOrder = skeleton.DrawOrder;
-			var drawOrderItems = skeleton.DrawOrder.Items;
-			float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A;
-			for (int i = 0, n = drawOrder.Count; i < n; i++) {
-				Slot slot = drawOrderItems[i];
-				RegionAttachment regionAttachment = slot.Attachment as RegionAttachment;
-				if (regionAttachment != null) {
-					BlendState blend = slot.Data.BlendMode == BlendMode.Additive ? BlendState.Additive : defaultBlendState;
-					if (device.BlendState != blend) {
-						End();
-						device.BlendState = blend;
-					}
-
-					RegionItem item = batcher.NextItem();
-
-					AtlasRegion region = (AtlasRegion)regionAttachment.RendererObject;
-					item.texture = (Texture2D)region.page.rendererObject;
-
-					Color color;
-					float a = skeletonA * slot.A;
-					if (premultipliedAlpha)
-						color = new Color(skeletonR * slot.R * a, skeletonG * slot.G * a, skeletonB * slot.B * a, a);
-					else
-						color = new Color(skeletonR * slot.R, skeletonG * slot.G, skeletonB * slot.B, a);
-					item.vertexTL.Color = color;
-					item.vertexBL.Color = color;
-					item.vertexBR.Color = color;
-					item.vertexTR.Color = color;
-
-					float[] vertices = this.vertices;
-					regionAttachment.ComputeWorldVertices(slot.Bone, vertices, 0, 2);
-					item.vertexTL.Position.X = vertices[0];
-					item.vertexTL.Position.Y = vertices[1];
-					item.vertexTL.Position.Z = 0;
-					item.vertexBL.Position.X = vertices[2];
-					item.vertexBL.Position.Y = vertices[3];
-					item.vertexBL.Position.Z = 0;
-					item.vertexBR.Position.X = vertices[4];
-					item.vertexBR.Position.Y = vertices[5];
-					item.vertexBR.Position.Z = 0;
-					item.vertexTR.Position.X = vertices[6];
-					item.vertexTR.Position.Y = vertices[7];
-					item.vertexTR.Position.Z = 0;
-
-					float[] uvs = regionAttachment.UVs;
-					item.vertexTL.TextureCoordinate.X = uvs[0];
-					item.vertexTL.TextureCoordinate.Y = uvs[1];
-					item.vertexBL.TextureCoordinate.X = uvs[2];
-					item.vertexBL.TextureCoordinate.Y = uvs[3];
-					item.vertexBR.TextureCoordinate.X = uvs[4];
-					item.vertexBR.TextureCoordinate.Y = uvs[5];
-					item.vertexTR.TextureCoordinate.X = uvs[6];
-					item.vertexTR.TextureCoordinate.Y = uvs[7];
-				}
-			}
-		}
-	}
-}

+ 206 - 199
spine-xna/src/SkeletonMeshRenderer.cs → spine-xna/src/SkeletonRenderer.cs

@@ -1,199 +1,206 @@
-/******************************************************************************
- * Spine Runtimes Software License
- * Version 2.3
- * 
- * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the
- * Software or develop new applications using the Software 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; 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.Collections.Generic;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework;
-
-namespace Spine {
-	/// <summary>Draws region and mesh attachments.</summary>
-	public class SkeletonMeshRenderer {
-		private const int TL = 0;
-		private const int TR = 1;
-		private const int BL = 2;
-		private const int BR = 3;
-
-		SkeletonClipping clipper = new SkeletonClipping();	
-		GraphicsDevice device;
-		MeshBatcher batcher;
-		RasterizerState rasterizerState;
-		float[] vertices = new float[8];
-		int[] quadTriangles = { 0, 1, 2, 2, 3, 0 };
-		BlendState defaultBlendState;
-
-		BasicEffect effect;
-		public BasicEffect Effect { get { return effect; } set { effect = value; } }
-
-		private bool premultipliedAlpha;
-		public bool PremultipliedAlpha { get { return premultipliedAlpha; } set { premultipliedAlpha = value; } }
-
-		public SkeletonMeshRenderer (GraphicsDevice device) {
-			this.device = device;
-
-			batcher = new MeshBatcher();
-
-			effect = new BasicEffect(device);
-			effect.World = Matrix.Identity;
-			effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
-			effect.TextureEnabled = true;
-			effect.VertexColorEnabled = true;
-
-			rasterizerState = new RasterizerState();
-			rasterizerState.CullMode = CullMode.None;
-
-			Bone.yDown = true;
-		}
-
-		public void Begin () {
-			defaultBlendState = premultipliedAlpha ? BlendState.AlphaBlend : BlendState.NonPremultiplied;
-
-			device.RasterizerState = rasterizerState;
-			device.BlendState = defaultBlendState;
-
-			effect.Projection = Matrix.CreateOrthographicOffCenter(0, device.Viewport.Width, device.Viewport.Height, 0, 1, 0);
-		}
-
-		public void End () {
-			foreach (EffectPass pass in effect.CurrentTechnique.Passes) {
-				pass.Apply();
-				batcher.Draw(device);
-			}
-		}
-
-		public void Draw(Skeleton skeleton) {
-			var drawOrder = skeleton.DrawOrder;
-			var drawOrderItems = skeleton.DrawOrder.Items;
-			float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A;
-			Color color = new Color();
-
-			for (int i = 0, n = drawOrder.Count; i < n; i++) {
-				Slot slot = drawOrderItems[i];
-				Attachment attachment = slot.Attachment;
-
-				float attachmentColorR, attachmentColorG, attachmentColorB, attachmentColorA;
-				Texture2D texture = null;
-				int verticesCount = 0;
-				float[] vertices = this.vertices;
-				int indicesCount = 0;
-				int[] indices = null;
-				float[] uvs = null;
-
-				if (attachment is RegionAttachment) {
-					RegionAttachment regionAttachment = (RegionAttachment)attachment;
-					attachmentColorR = regionAttachment.R; attachmentColorG = regionAttachment.G; attachmentColorB = regionAttachment.B; attachmentColorA = regionAttachment.A;
-					AtlasRegion region = (AtlasRegion)regionAttachment.RendererObject;
-					texture = (Texture2D)region.page.rendererObject;
-					verticesCount = 4;
-					regionAttachment.ComputeWorldVertices(slot.Bone, vertices, 0, 2);
-					indicesCount = 6;
-					indices = quadTriangles;
-					uvs = regionAttachment.UVs;
-				}
-				else if (attachment is MeshAttachment) {
-					MeshAttachment mesh = (MeshAttachment)attachment;
-					attachmentColorR = mesh.R; attachmentColorG = mesh.G; attachmentColorB = mesh.B; attachmentColorA = mesh.A;
-					AtlasRegion region = (AtlasRegion)mesh.RendererObject;
-					texture = (Texture2D)region.page.rendererObject;
-					int vertexCount = mesh.WorldVerticesLength;
-					if (vertices.Length < vertexCount) vertices = new float[vertexCount];
-					verticesCount = vertexCount >> 1;
-					mesh.ComputeWorldVertices(slot, vertices);
-					indicesCount = mesh.Triangles.Length;
-					indices = mesh.Triangles;
-					uvs = mesh.UVs;
-				}
-				else if (attachment is ClippingAttachment) {
-					ClippingAttachment clip = (ClippingAttachment)attachment;
-					clipper.ClipStart(slot, clip);
-					continue;
-				}
-				else {
-					continue;
-				}
-
-				// set blend state
-				BlendState blend = slot.Data.BlendMode == BlendMode.Additive ? BlendState.Additive : defaultBlendState;
-				if (device.BlendState != blend) {
-					//End();
-					//device.BlendState = blend;
-				}
-
-				// calculate color
-				float a = skeletonA * slot.A * attachmentColorA;
-				if (premultipliedAlpha) {
-					color = new Color(
-							skeletonR * slot.R * attachmentColorR * a,
-							skeletonG * slot.G * attachmentColorG * a,
-							skeletonB * slot.B * attachmentColorB * a, a);
-				}
-				else {
-					color = new Color(
-							skeletonR * slot.R * attachmentColorR,
-							skeletonG * slot.G * attachmentColorG,
-							skeletonB * slot.B * attachmentColorB, a);
-				}
-
-				// clip
-				if (clipper.IsClipping()) {
-					clipper.ClipTriangles(vertices, verticesCount << 1, indices, indicesCount, uvs);
-					vertices = clipper.ClippedVertices.Items;
-					verticesCount = clipper.ClippedVertices.Count >> 1;
-					indices = clipper.ClippedTriangles.Items;
-					indicesCount = clipper.ClippedTriangles.Count;
-					uvs = clipper.ClippedUVs.Items;
-				}
-
-				if (verticesCount == 0 || indicesCount == 0)
-					continue;
-
-				// submit to batch
-				MeshItem item = batcher.NextItem(verticesCount, indicesCount);
-				item.texture = texture;
-				for (int ii = 0, nn = indicesCount; ii < nn; ii++) {
-					item.triangles[ii] = indices[ii];
-				}
-				VertexPositionColorTexture[] itemVertices = item.vertices;
-				for (int ii = 0, v = 0, nn = verticesCount << 1; v < nn; ii++, v += 2) {
-					itemVertices[ii].Color = color;
-					itemVertices[ii].Position.X = vertices[v];
-					itemVertices[ii].Position.Y = vertices[v + 1];
-					itemVertices[ii].Position.Z = 0;
-					itemVertices[ii].TextureCoordinate.X = uvs[v];
-					itemVertices[ii].TextureCoordinate.Y = uvs[v + 1];
-				}
-
-				clipper.ClipEnd(slot);
-			}
-			clipper.ClipEnd();
-		}
-	}
-}
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.3
+ * 
+ * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the
+ * Software or develop new applications using the Software 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; 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.Collections.Generic;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework;
+
+namespace Spine {
+	/// <summary>Draws region and mesh attachments.</summary>
+	public class SkeletonRenderer {
+		private const int TL = 0;
+		private const int TR = 1;
+		private const int BL = 2;
+		private const int BR = 3;
+
+		SkeletonClipping clipper = new SkeletonClipping();	
+		GraphicsDevice device;
+		MeshBatcher batcher;
+		RasterizerState rasterizerState;
+		float[] vertices = new float[8];
+		int[] quadTriangles = { 0, 1, 2, 2, 3, 0 };
+		BlendState defaultBlendState;
+
+		Effect effect;
+		public Effect Effect { get { return effect; } set { effect = value; } }
+
+		private bool premultipliedAlpha;
+		public bool PremultipliedAlpha { get { return premultipliedAlpha; } set { premultipliedAlpha = value; } }
+
+		public SkeletonRenderer (GraphicsDevice device) {
+			this.device = device;
+
+			batcher = new MeshBatcher();
+
+			var basicEffect = new BasicEffect(device);
+			basicEffect.World = Matrix.Identity;
+			basicEffect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
+			basicEffect.TextureEnabled = true;
+			basicEffect.VertexColorEnabled = true;
+			effect = basicEffect;
+
+			rasterizerState = new RasterizerState();
+			rasterizerState.CullMode = CullMode.None;
+
+			Bone.yDown = true;
+		}
+
+		public void Begin () {
+			defaultBlendState = premultipliedAlpha ? BlendState.AlphaBlend : BlendState.NonPremultiplied;
+
+			device.RasterizerState = rasterizerState;
+			device.BlendState = defaultBlendState;
+
+			if (effect is BasicEffect) ((BasicEffect)effect).Projection = Matrix.CreateOrthographicOffCenter(0, device.Viewport.Width, device.Viewport.Height, 0, 1, 0);
+		}
+
+		public void End () {
+			foreach (EffectPass pass in effect.CurrentTechnique.Passes) {
+				pass.Apply();
+				batcher.Draw(device);
+			}
+		}
+
+		public void Draw(Skeleton skeleton) {
+			var drawOrder = skeleton.DrawOrder;
+			var drawOrderItems = skeleton.DrawOrder.Items;
+			float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A;
+			Color color = new Color();
+
+			for (int i = 0, n = drawOrder.Count; i < n; i++) {
+				Slot slot = drawOrderItems[i];
+				Attachment attachment = slot.Attachment;
+
+				float attachmentColorR, attachmentColorG, attachmentColorB, attachmentColorA;
+				Texture2D texture = null;
+				int verticesCount = 0;
+				float[] vertices = this.vertices;
+				int indicesCount = 0;
+				int[] indices = null;
+				float[] uvs = null;
+
+				if (attachment is RegionAttachment) {
+					RegionAttachment regionAttachment = (RegionAttachment)attachment;
+					attachmentColorR = regionAttachment.R; attachmentColorG = regionAttachment.G; attachmentColorB = regionAttachment.B; attachmentColorA = regionAttachment.A;
+					AtlasRegion region = (AtlasRegion)regionAttachment.RendererObject;
+					texture = (Texture2D)region.page.rendererObject;
+					verticesCount = 4;
+					regionAttachment.ComputeWorldVertices(slot.Bone, vertices, 0, 2);
+					indicesCount = 6;
+					indices = quadTriangles;
+					uvs = regionAttachment.UVs;
+				}
+				else if (attachment is MeshAttachment) {
+					MeshAttachment mesh = (MeshAttachment)attachment;
+					attachmentColorR = mesh.R; attachmentColorG = mesh.G; attachmentColorB = mesh.B; attachmentColorA = mesh.A;
+					AtlasRegion region = (AtlasRegion)mesh.RendererObject;
+					texture = (Texture2D)region.page.rendererObject;
+					int vertexCount = mesh.WorldVerticesLength;
+					if (vertices.Length < vertexCount) vertices = new float[vertexCount];
+					verticesCount = vertexCount >> 1;
+					mesh.ComputeWorldVertices(slot, vertices);
+					indicesCount = mesh.Triangles.Length;
+					indices = mesh.Triangles;
+					uvs = mesh.UVs;
+				}
+				else if (attachment is ClippingAttachment) {
+					ClippingAttachment clip = (ClippingAttachment)attachment;
+					clipper.ClipStart(slot, clip);
+					continue;
+				}
+				else {
+					continue;
+				}
+
+				// set blend state
+				BlendState blend = slot.Data.BlendMode == BlendMode.Additive ? BlendState.Additive : defaultBlendState;
+				if (device.BlendState != blend) {
+					//End();
+					//device.BlendState = blend;
+				}
+
+				// calculate color
+				float a = skeletonA * slot.A * attachmentColorA;
+				if (premultipliedAlpha) {
+					color = new Color(
+							skeletonR * slot.R * attachmentColorR * a,
+							skeletonG * slot.G * attachmentColorG * a,
+							skeletonB * slot.B * attachmentColorB * a, a);
+				}
+				else {
+					color = new Color(
+							skeletonR * slot.R * attachmentColorR,
+							skeletonG * slot.G * attachmentColorG,
+							skeletonB * slot.B * attachmentColorB, a);
+				}
+
+				Color darkColor = new Color();
+				if (slot.HasSecondColor) {
+					darkColor = new Color(slot.R2, slot.G2, slot.B2);
+				}
+
+				// clip
+				if (clipper.IsClipping()) {
+					clipper.ClipTriangles(vertices, verticesCount << 1, indices, indicesCount, uvs);
+					vertices = clipper.ClippedVertices.Items;
+					verticesCount = clipper.ClippedVertices.Count >> 1;
+					indices = clipper.ClippedTriangles.Items;
+					indicesCount = clipper.ClippedTriangles.Count;
+					uvs = clipper.ClippedUVs.Items;
+				}
+
+				if (verticesCount == 0 || indicesCount == 0)
+					continue;
+
+				// submit to batch
+				MeshItem item = batcher.NextItem(verticesCount, indicesCount);
+				item.texture = texture;
+				for (int ii = 0, nn = indicesCount; ii < nn; ii++) {
+					item.triangles[ii] = indices[ii];
+				}
+				VertexPositionColorTextureColor[] itemVertices = item.vertices;
+				for (int ii = 0, v = 0, nn = verticesCount << 1; v < nn; ii++, v += 2) {
+					itemVertices[ii].Color = color;
+					itemVertices[ii].Color2 = darkColor;
+					itemVertices[ii].Position.X = vertices[v];
+					itemVertices[ii].Position.Y = vertices[v + 1];
+					itemVertices[ii].Position.Z = 0;
+					itemVertices[ii].TextureCoordinate.X = uvs[v];
+					itemVertices[ii].TextureCoordinate.Y = uvs[v + 1];
+				}
+
+				clipper.ClipEnd(slot);
+			}
+			clipper.ClipEnd();
+		}
+	}
+}