Merge branch '3.6' into 3.7-beta

This commit is contained in:
badlogic 2017-07-21 16:49:12 +02:00
commit f5965b0fdf
28 changed files with 1011 additions and 83 deletions

1
.gitignore vendored
View File

@ -55,6 +55,7 @@ spine-xna/obj
spine-xna/example/bin
spine-xna/example/obj
spine-xna/example-content/obj/
spine-xna/.vs/
spine-unity/Assets/spine-csharp*
!spine-unity/Assets/spine-csharp/Place spine-csharp src here.*

View File

@ -142,6 +142,7 @@
* 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`
* Added support for `IVertexEffect` to modify vertices of skeletons on the CPU. `IVertexEffect` instances can be set on the `SkeletonRenderer`. See example project.
* Added `SkeletonDebugRenderer`
## Java
* **Breaking changes**

View File

@ -28,6 +28,10 @@
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif
using System;
using System.Collections.Generic;
using System.IO;
@ -44,7 +48,7 @@ namespace Spine {
List<AtlasRegion> regions = new List<AtlasRegion>();
TextureLoader textureLoader;
#if !(UNITY_5 || UNITY_4 || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1) // !UNITY
#if !(IS_UNITY)
#if WINDOWS_STOREAPP
private async Task ReadFile(string path, TextureLoader textureLoader) {
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
@ -82,7 +86,7 @@ namespace Spine {
}
#endif // WINDOWS_STOREAPP
#endif // !(UNITY)
#endif
public Atlas (TextReader reader, string dir, TextureLoader textureLoader) {
Load(reader, dir, textureLoader);

View File

@ -28,7 +28,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if (UNITY_5 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif

View File

@ -180,9 +180,9 @@ namespace Spine {
if (clippingArea.Count % 4 >= 2) {
input = output;
output = scratch;
}
else
} else {
input = scratch;
}
input.Clear();
input.Add(x1);
@ -251,14 +251,14 @@ namespace Spine {
for (int i = 0, n = output.Count - 2; i < n; i++) {
originalOutput.Add(output.Items[i]);
}
}
else
} else {
originalOutput.Resize(originalOutput.Count - 2);
}
return clipped;
}
static void MakeClockwise (ExposedList<float> polygon) {
public static void MakeClockwise (ExposedList<float> polygon) {
float[] vertices = polygon.Items;
int verticeslength = polygon.Count;

View File

@ -31,7 +31,7 @@
using System;
namespace Spine {
internal class Triangulator {
public class Triangulator {
private readonly ExposedList<ExposedList<float>> convexPolygons = new ExposedList<ExposedList<float>>();
private readonly ExposedList<ExposedList<int>> convexPolygonsIndices = new ExposedList<ExposedList<int>>();
@ -84,6 +84,7 @@ namespace Spine {
}
break;
}
outer:
if (next == 0) {
do {
@ -97,7 +98,6 @@ namespace Spine {
i = next;
next = (next + 1) % vertexCount;
}
outer:
// Cut ear tip.
triangles.Add(indices[(vertexCount + i - 1) % vertexCount]);

View File

@ -56,7 +56,8 @@ public class SkeletonClipping {
float[] vertices = clippingPolygon.setSize(n);
clip.computeWorldVertices(slot, 0, n, vertices, 0, 2);
makeClockwise(clippingPolygon);
clippingPolygons = triangulator.decompose(clippingPolygon, triangulator.triangulate(clippingPolygon));
ShortArray triangles = triangulator.triangulate(clippingPolygon);
clippingPolygons = triangulator.decompose(clippingPolygon, triangles);
for (FloatArray polygon : clippingPolygons) {
makeClockwise(polygon);
polygon.add(polygon.items[0]);

View File

@ -21,7 +21,7 @@ spine-ue4 does not support multiply and screen blending. spine-ue4 does not supp
1. Create a new Unreal Engine code project. You don't need to write C++, but the code project is needed for the plugin to compile. See the [Unreal Engine documentation](https://docs.unrealengine.com/latest/INT/) or have a look at the example in this repository.
2. Download the Spine Runtimes source using git (`git clone https://github.com/esotericsoftware/spine-runtimes`) or download it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/3.6.zip)
3. Copy the `Plugins` folder from this directory to your new project's root directory.
4. Copy the `spine-c` folder from this repositories root directory to your project's `Plugins/SpinePlugin/Sources/SpinePlugin/Public/` directory.
4. Copy the folder `spine-runtimes/spine-c/spine-c` to your project's `Plugins/SpinePlugin/Source/SpinePlugin/Public/` folder.
5. Open the Unreal Project in the Unreal Editor
See the [Spine Runtimes documentation](http://esotericsoftware.com/spine-documentation#runtimesTitle) on how to use the APIs or check out the Spine UE4 example.

View File

@ -0,0 +1,288 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 8
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 3
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_TemporalCoherenceThreshold: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 0
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 8
m_Resolution: 2
m_BakeResolution: 40
m_TextureWidth: 1024
m_TextureHeight: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 3
m_BakeBackend: 0
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVRBounces: 2
m_PVRFiltering: 0
m_PVRFilteringMode: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousColorSigma: 1
m_PVRFilteringAtrousNormalSigma: 1
m_PVRFilteringAtrousPositionSigma: 1
m_LightingDataAsset: {fileID: 0}
m_ShadowMaskMode: 2
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &433620963
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 5
m_Component:
- component: {fileID: 433620968}
- component: {fileID: 433620967}
- component: {fileID: 433620966}
- component: {fileID: 433620965}
- component: {fileID: 433620964}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &433620964
AudioListener:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 433620963}
m_Enabled: 1
--- !u!124 &433620965
Behaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 433620963}
m_Enabled: 1
--- !u!92 &433620966
Behaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 433620963}
m_Enabled: 1
--- !u!20 &433620967
Camera:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 433620963}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 1
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
m_StereoMirrorMode: 0
--- !u!4 &433620968
Transform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 433620963}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &651278528
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 5
m_Component:
- component: {fileID: 651278530}
- component: {fileID: 651278529}
m_Layer: 0
m_Name: 2 DataAssets from Exports
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &651278529
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 651278528}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: bb0837af7345d504db63d0c662fd12dc, type: 3}
m_Name:
m_EditorClassIdentifier:
skeletonJson: {fileID: 4900000, guid: e3b64d7eaf0de4e45a00b7065166554d, type: 3}
atlasText: {fileID: 4900000, guid: 5c0a5c36970a46e4d8378760ab4a4cfc, type: 3}
textures:
- {fileID: 2800000, guid: 49bb65eefe08e424bbf7a38bc98ec638, type: 3}
materialPropertySource: {fileID: 2100000, guid: 365eb017d7ae7134d820c8b808eeb121,
type: 2}
--- !u!4 &651278530
Transform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 651278528}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1807176298
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
serializedVersion: 5
m_Component:
- component: {fileID: 1807176300}
- component: {fileID: 1807176299}
m_Layer: 0
m_Name: 1 Spawn from SkeletonData
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1807176299
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1807176298}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7193e2e00836b124191dcae19e6c9741, type: 3}
m_Name:
m_EditorClassIdentifier:
skeletonDataAsset: {fileID: 11400000, guid: 57484171e9b9c7243aa3117bc663e7b9, type: 2}
count: 50
startingAnimation: animation
--- !u!4 &1807176300
Transform:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1807176298}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 59dc5776e19e2f041b1cac961a86924f
timeCreated: 1500249265
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.Examples {
public class DataAssetsFromExportsExample : MonoBehaviour {
public TextAsset skeletonJson;
public TextAsset atlasText;
public Texture2D[] textures;
public Material materialPropertySource;
AtlasAsset runtimeAtlasAsset;
SkeletonDataAsset runtimeSkeletonDataAsset;
SkeletonAnimation runtimeSkeletonAnimation;
void CreateRuntimeAssetsAndGameObject () {
// 1. Create the AtlasAsset (needs atlas text asset and textures, and materials/shader);
// 2. Create SkeletonDataAsset (needs json or binary asset file, and an AtlasAsset)
// 3. Create SkeletonAnimation (needs a valid SkeletonDataAsset)
runtimeAtlasAsset = AtlasAsset.CreateRuntimeInstance(atlasText, textures, materialPropertySource, true);
runtimeSkeletonDataAsset = SkeletonDataAsset.CreateRuntimeInstance(skeletonJson, runtimeAtlasAsset, true);
}
IEnumerator Start () {
CreateRuntimeAssetsAndGameObject();
runtimeSkeletonDataAsset.GetSkeletonData(false); // preload.
yield return new WaitForSeconds(0.5f);
runtimeSkeletonAnimation = SkeletonAnimation.NewSkeletonAnimationGameObject(runtimeSkeletonDataAsset);
// Extra Stuff
runtimeSkeletonAnimation.Initialize(false);
runtimeSkeletonAnimation.Skeleton.SetSkin("base");
runtimeSkeletonAnimation.Skeleton.SetSlotsToSetupPose();
runtimeSkeletonAnimation.AnimationState.SetAnimation(0, "run", true);
runtimeSkeletonAnimation.GetComponent<MeshRenderer>().sortingOrder = 10;
runtimeSkeletonAnimation.transform.Translate(Vector3.down * 2);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: bb0837af7345d504db63d0c662fd12dc
timeCreated: 1500249349
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -39,7 +39,7 @@ namespace Spine.Unity.Examples {
#region Inspector
[SpineSkin]
public string baseSkinName = "base";
public string templateAttachmentsSkin = "base";
public Material sourceMaterial; // This will be used as the basis for shader and material property settings.
[Header("Visor")]
@ -83,26 +83,25 @@ namespace Spine.Unity.Examples {
// STEP 0: PREPARE SKINS
// Let's prepare a new skin to be our custom skin with equips/customizations. We get a clone so our original skins are unaffected.
customSkin = customSkin ?? new Skin("custom skin"); // This requires that all customizations are done with skin placeholders defined in Spine.
//customSkin = customSkin ?? skeleton.UnshareSkin(true, false, skeletonAnimation.AnimationState); // use this if you are not customizing on the default skin and don't plan to remove
// Next let's
var baseSkin = skeleton.Data.FindSkin(baseSkinName);
//customSkin = customSkin ?? skeleton.UnshareSkin(true, false, skeletonAnimation.AnimationState); // use this if you are not customizing on the default skin.
var templateSkin = skeleton.Data.FindSkin(templateAttachmentsSkin);
// STEP 1: "EQUIP" ITEMS USING SPRITES
// STEP 1.1 Find the original attachment.
// Step 1.2 Get a clone of the original attachment.
// STEP 1.1 Find the original/template attachment.
// Step 1.2 Get a clone of the original/template attachment.
// Step 1.3 Apply the Sprite image to the clone.
// Step 1.4 Add the remapped clone to the new custom skin.
// Let's do this for the visor.
int visorSlotIndex = skeleton.FindSlotIndex(visorSlot); // You can access GetAttachment and SetAttachment via string, but caching the slotIndex is faster.
Attachment baseAttachment = baseSkin.GetAttachment(visorSlotIndex, visorKey); // STEP 1.1
Attachment newAttachment = baseAttachment.GetRemappedClone(visorSprite, sourceMaterial); // STEP 1.2 - 1.3
Attachment templateAttachment = templateSkin.GetAttachment(visorSlotIndex, visorKey); // STEP 1.1
Attachment newAttachment = templateAttachment.GetRemappedClone(visorSprite, sourceMaterial); // STEP 1.2 - 1.3
customSkin.SetAttachment(visorSlotIndex, visorKey, newAttachment); // STEP 1.4
// And now for the gun.
int gunSlotIndex = skeleton.FindSlotIndex(gunSlot);
Attachment baseGun = baseSkin.GetAttachment(gunSlotIndex, gunKey); // STEP 1.1
Attachment newGun = baseGun.GetRemappedClone(gunSprite, sourceMaterial); // STEP 1.2 - 1.3
Attachment templateGun = templateSkin.GetAttachment(gunSlotIndex, gunKey); // STEP 1.1
Attachment newGun = templateGun.GetRemappedClone(gunSprite, sourceMaterial); // STEP 1.2 - 1.3
if (newGun != null) customSkin.SetAttachment(gunSlotIndex, gunKey, newGun); // STEP 1.4
// customSkin.RemoveAttachment(gunSlotIndex, gunKey); // To remove an item.
@ -119,17 +118,17 @@ namespace Spine.Unity.Examples {
// Under the hood, this relies on
if (repack) {
var repackedSkin = new Skin("repacked skin");
repackedSkin.Append(skeleton.Data.DefaultSkin);
repackedSkin.Append(customSkin);
repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas);
skeleton.SetSkin(repackedSkin);
repackedSkin.Append(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders)
repackedSkin.Append(customSkin); // Include your new custom skin.
repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas); // Pack all the items in the skin.
skeleton.SetSkin(repackedSkin); // Assign the repacked skin to your Skeleton.
if (bbFollower != null) bbFollower.Initialize(true);
} else {
skeleton.SetSkin(customSkin);
skeleton.SetSkin(customSkin); // Just use the custom skin directly.
}
skeleton.SetSlotsToSetupPose();
skeletonAnimation.Update(0);
skeleton.SetSlotsToSetupPose(); // Use the pose from setup pose.
skeletonAnimation.Update(0); // Use the pose in the currently active animation.
Resources.UnloadUnusedAssets();
}

View File

@ -0,0 +1,42 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.Examples {
public class SpawnFromSkeletonDataExample : MonoBehaviour {
public SkeletonDataAsset skeletonDataAsset;
[Range(0, 100)]
public int count = 20;
[SpineAnimation(dataField:"skeletonDataAsset")]
public string startingAnimation;
IEnumerator Start () {
if (skeletonDataAsset == null) yield break;
skeletonDataAsset.GetSkeletonData(false); // Preload SkeletonDataAsset.
yield return new WaitForSeconds(1f); // Pretend stuff is happening.
var spineAnimation = skeletonDataAsset.GetSkeletonData(false).FindAnimation(startingAnimation);
for (int i = 0; i < count; i++) {
var sa = SkeletonAnimation.NewSkeletonAnimationGameObject(skeletonDataAsset); // Spawn a new SkeletonAnimation GameObject.
DoExtraStuff(sa, spineAnimation); // optional stuff for fun.
sa.gameObject.name = i.ToString();
yield return new WaitForSeconds(1f/8f);
}
}
void DoExtraStuff (SkeletonAnimation sa, Spine.Animation spineAnimation) {
sa.transform.localPosition = Random.insideUnitCircle * 6f;
sa.transform.SetParent(this.transform, false);
if (spineAnimation != null) {
sa.Initialize(false);
sa.AnimationState.SetAnimation(0, spineAnimation, true);
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7193e2e00836b124191dcae19e6c9741
timeCreated: 1500249330
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -13,8 +13,8 @@ MonoBehaviour:
m_EditorClassIdentifier:
atlasAssets:
- {fileID: 11400000, guid: 6f86779b1deba7c4aaec1f5895510b57, type: 2}
skeletonJSON: {fileID: 4900000, guid: 95c98e245823e2243a1d8e7a1ef16c51, type: 3}
scale: 0.01
skeletonJSON: {fileID: 4900000, guid: 95c98e245823e2243a1d8e7a1ef16c51, type: 3}
fromAnimation: []
toAnimation: []
duration: []

View File

@ -0,0 +1,76 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_Name: Runtime Template Material
m_Shader: {fileID: 4800000, guid: 1e8a610c9e01c3648bac42585e5fc676, type: 3}
m_ShaderKeywords:
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 365eb017d7ae7134d820c8b808eeb121
timeCreated: 1500250447
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -60,10 +60,7 @@ namespace Spine.Unity {
/// <summary>
/// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches. <seealso cref="Spine.Unity.AtlasAsset.CreateRuntimeInstance(TextAsset, Material[], bool)"/></summary>
public static AtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Shader shader, bool initialize) {
if (shader == null)
shader = Shader.Find("Spine/Skeleton");
public static AtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Material materialPropertySource, bool initialize) {
// Get atlas page names.
string atlasString = atlasText.text;
atlasString = atlasString.Replace("\r", "");
@ -84,7 +81,7 @@ namespace Spine.Unity {
for (int j = 0, m = textures.Length; j < m; j++) {
if (string.Equals(pageName, textures[j].name, System.StringComparison.OrdinalIgnoreCase)) {
// Match found.
mat = new Material(shader);
mat = new Material(materialPropertySource);
mat.mainTexture = textures[j];
break;
}
@ -102,14 +99,12 @@ namespace Spine.Unity {
/// <summary>
/// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches. <seealso cref="Spine.Unity.AtlasAsset.CreateRuntimeInstance(TextAsset, Material[], bool)"/></summary>
public static AtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Material materialPropertySource, bool initialize) {
var shader = materialPropertySource.shader;
var oa = CreateRuntimeInstance(atlasText, textures, shader, initialize);
public static AtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Shader shader, bool initialize) {
if (shader == null)
shader = Shader.Find("Spine/Skeleton");
foreach (var m in oa.materials) {
m.CopyPropertiesFromMaterial(materialPropertySource);
m.shaderKeywords = materialPropertySource.shaderKeywords;
}
Material materialProperySource = new Material(shader);
var oa = CreateRuntimeInstance(atlasText, textures, materialProperySource, initialize);
return oa;
}

View File

@ -1213,7 +1213,7 @@ namespace Spine.Unity.Editor {
#endregion
#region Checking Methods
static int[][] compatibleVersions = { new[] {3, 6, 0} };
static int[][] compatibleVersions = { new[] {3, 6, 0}, new[] {3, 5, 0} };
//static bool isFixVersionRequired = false;
static bool CheckForValidSkeletonData (string skeletonJSONPath) {
@ -1272,10 +1272,10 @@ namespace Spine.Unity.Editor {
// Version warning
if (isSpineData) {
string runtimeVersion = compatibleVersions[0][0] + "." + compatibleVersions[0][1];
string runtimeVersionDebugString = compatibleVersions[0][0] + "." + compatibleVersions[0][1];
if (string.IsNullOrEmpty(rawVersion)) {
Debug.LogWarningFormat("Skeleton '{0}' has no version information. It may be incompatible with your runtime version: spine-unity v{1}", asset.name, runtimeVersion);
Debug.LogWarningFormat("Skeleton '{0}' has no version information. It may be incompatible with your runtime version: spine-unity v{1}", asset.name, runtimeVersionDebugString);
} else {
string[] versionSplit = rawVersion.Split('.');
bool match = false;
@ -1292,7 +1292,7 @@ namespace Spine.Unity.Editor {
}
if (!match)
Debug.LogWarningFormat("Skeleton '{0}' (exported with Spine {1}) may be incompatible with your runtime version: spine-unity v{2}", asset.name, rawVersion, runtimeVersion);
Debug.LogWarningFormat("Skeleton '{0}' (exported with Spine {1}) may be incompatible with your runtime version: spine-unity v{2}", asset.name, rawVersion, runtimeVersionDebugString);
}
}
@ -1476,11 +1476,18 @@ namespace Spine.Unity.Editor {
#region TK2D Support
const string SPINE_TK2D_DEFINE = "SPINE_TK2D";
static bool IsInvalidGroup (BuildTargetGroup group) {
int gi = (int)group;
return
gi == 15 || gi == 16
||
group == BuildTargetGroup.Unknown;
}
static void EnableTK2D () {
bool added = false;
foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
int gi = (int)group;
if (gi == 15 || gi == 16 || group == BuildTargetGroup.Unknown)
if (IsInvalidGroup(group))
continue;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
@ -1506,8 +1513,7 @@ namespace Spine.Unity.Editor {
static void DisableTK2D () {
bool removed = false;
foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) {
int gi = (int)group;
if (gi == 15 || gi == 16 || group == BuildTargetGroup.Unknown)
if (IsInvalidGroup(group))
continue;
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);

View File

@ -502,16 +502,16 @@ namespace Spine.Unity {
} else {
var mesh = attachment as MeshAttachment;
if (mesh != null) {
int meshVertexCount = mesh.worldVerticesLength;
if (workingVerts.Length < meshVertexCount) {
workingVerts = new float[meshVertexCount];
int meshVerticesLength = mesh.worldVerticesLength;
if (workingVerts.Length < meshVerticesLength) {
workingVerts = new float[meshVerticesLength];
this.tempVerts = workingVerts;
}
mesh.ComputeWorldVertices(slot, 0, meshVertexCount, workingVerts, 0); //meshAttachment.ComputeWorldVertices(slot, tempVerts);
mesh.ComputeWorldVertices(slot, 0, meshVerticesLength, workingVerts, 0); //meshAttachment.ComputeWorldVertices(slot, tempVerts);
uvs = mesh.uvs;
attachmentTriangleIndices = mesh.triangles;
c.r = mesh.r; c.g = mesh.g; c.b = mesh.b; c.a = mesh.a;
attachmentVertexCount = meshVertexCount >> 1; // meshVertexCount / 2;
attachmentVertexCount = meshVerticesLength >> 1; // meshVertexCount / 2;
attachmentIndexCount = mesh.triangles.Length;
} else {
if (useClipping) {

View File

@ -469,9 +469,9 @@ namespace Spine.Unity.Modules.AttachmentTools {
a.SetRegion(repackedRegions[regionIndexes[i]]);
}
// Clean up
foreach (var ttp in texturesToPack)
UnityEngine.Object.Destroy(ttp);
// // Clean up
// foreach (var ttp in texturesToPack)
// UnityEngine.Object.Destroy(ttp);
t = newTexture;
m = newMaterial;
@ -482,20 +482,38 @@ namespace Spine.Unity.Modules.AttachmentTools {
return Sprite.Create(ar.GetMainTexture(), ar.GetUnityRect(), new Vector2(0.5f, 0.5f), pixelsPerUnit);
}
static Dictionary<AtlasRegion, Texture2D> CachedRegionTextures = new Dictionary<AtlasRegion, Texture2D>();
static List<Texture2D> CachedRegionTexturesList = new List<Texture2D>();
public static void ClearCache () {
foreach (var t in CachedRegionTexturesList) {
UnityEngine.Object.Destroy(t);
}
CachedRegionTextures.Clear();
CachedRegionTexturesList.Clear();
}
/// <summary>Creates a new Texture2D object based on an AtlasRegion.
/// If applyImmediately is true, Texture2D.Apply is called immediately after the Texture2D is filled with data.</summary>
public static Texture2D ToTexture (this AtlasRegion ar, bool applyImmediately = true, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) {
Texture2D output;
CachedRegionTextures.TryGetValue(ar, out output);
if (output == null) {
Texture2D sourceTexture = ar.GetMainTexture();
Rect r = ar.GetUnityRect(sourceTexture.height);
int width = (int)r.width;
int height = (int)r.height;
Texture2D output = new Texture2D(width, height, textureFormat, mipmaps);
output = new Texture2D(width, height, textureFormat, mipmaps);
output.name = ar.name;
Color[] pixelBuffer = sourceTexture.GetPixels((int)r.x, (int)r.y, width, height);
output.SetPixels(pixelBuffer);
CachedRegionTextures.Add(ar, output);
CachedRegionTexturesList.Add(output);
if (applyImmediately)
output.Apply();
}
return output;
}

View File

@ -122,6 +122,9 @@
<None Include="data\coin-pro.skel">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="data\goblins-mesh.atlas">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="data\goblins.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@ -195,9 +198,6 @@
<None Include="data\coin-pro.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="data\goblins-mesh.atlas">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="data\goblins-pro.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@ -43,6 +43,7 @@ namespace Spine {
public class Example : Microsoft.Xna.Framework.Game {
GraphicsDeviceManager graphics;
SkeletonRenderer skeletonRenderer;
SkeletonDebugRenderer skeletonDebugRenderer;
Skeleton skeleton;
Slot headSlot;
AnimationState state;
@ -79,12 +80,17 @@ namespace Spine {
skeletonRenderer.PremultipliedAlpha = false;
skeletonRenderer.Effect = spineEffect;
skeletonDebugRenderer = new SkeletonDebugRenderer(GraphicsDevice);
skeletonDebugRenderer.DisableAll();
skeletonDebugRenderer.DrawClipping = true;
// String name = "spineboy-ess";
// String name = "goblins-pro";
String name = "goblins-pro";
// String name = "raptor-pro";
// String name = "tank-pro";
String name = "coin-pro";
// String name = "coin-pro";
String atlasName = name.Replace("-pro", "").Replace("-ess", "");
if (name == "goblins-pro") atlasName = "goblins-mesh";
bool binaryData = false;
Atlas atlas = new Atlas(assetsFolder + atlasName + ".atlas", new XnaTextureLoader(GraphicsDevice));
@ -122,27 +128,28 @@ namespace Spine {
state.Complete += Complete;
state.Event += Event;
state.SetAnimation(0, "test", false);
state.SetAnimation(0, "run", true);
TrackEntry entry = state.AddAnimation(0, "jump", false, 0);
entry.End += End; // Event handling for queued animations.
state.AddAnimation(0, "run", true, 0);
}
else if (name == "raptor-pro") {
state.SetAnimation(0, "walk", true);
state.AddAnimation(1, "gungrab", false, 2);
state.AddAnimation(1, "gun-grab", false, 2);
}
else if (name == "coin-pro") {
state.SetAnimation(0, "rotate", true);
}
else if (name == "tank-pro") {
skeleton.X += 300;
state.SetAnimation(0, "drive", true);
}
else {
state.SetAnimation(0, "walk", true);
}
skeleton.X = 400 + (name == "tank-pro" ? 300: 0);
skeleton.Y = GraphicsDevice.Viewport.Height;
skeleton.X += 400;
skeleton.Y += GraphicsDevice.Viewport.Height;
skeleton.UpdateWorldTransform();
headSlot = skeleton.FindSlot("head");
@ -176,6 +183,11 @@ namespace Spine {
skeletonRenderer.Draw(skeleton);
skeletonRenderer.End();
skeletonDebugRenderer.Effect.Projection = Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 1, 0);
skeletonDebugRenderer.Begin();
skeletonDebugRenderer.Draw(skeleton);
skeletonDebugRenderer.End();
bounds.Update(skeleton, true);
MouseState mouse = Mouse.GetState();
if (headSlot != null) {

View File

@ -111,6 +111,8 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="src\MeshBatcher.cs" />
<Compile Include="src\VertexEffect.cs" />
<Compile Include="src\ShapeRenderer.cs" />
<Compile Include="src\SkeletonDebugRenderer.cs" />
<Compile Include="src\XnaTextureLoader.cs" />
<Compile Include="src\Util.cs" />
</ItemGroup>

View File

@ -0,0 +1,168 @@
/******************************************************************************
* 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 Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Spine {
/// <summary>
/// Batch drawing of lines and shapes that can be derrived from lines.
///
/// Call drawing methods in between Begin()/End()
/// </summary>
public class ShapeRenderer {
GraphicsDevice device;
List<VertexPositionColor> vertices = new List<VertexPositionColor>();
Color color = Color.White;
BasicEffect effect;
public BasicEffect Effect { get { return effect; } set { effect = value; } }
public ShapeRenderer(GraphicsDevice device) {
this.device = device;
this.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 = false;
effect.VertexColorEnabled = true;
}
public void SetColor(Color color) {
this.color = color;
}
public void Begin() {
device.RasterizerState = new RasterizerState();
device.BlendState = BlendState.AlphaBlend;
}
public void Line(float x1, float y1, float x2, float y2) {
vertices.Add(new VertexPositionColor(new Vector3(x1, y1, 0), color));
vertices.Add(new VertexPositionColor(new Vector3(x2, y2, 0), color));
}
/** Calls {@link #circle(float, float, float, int)} by estimating the number of segments needed for a smooth circle. */
public void Circle(float x, float y, float radius) {
Circle(x, y, radius, Math.Max(1, (int)(6 * (float)Math.Pow(radius, 1.0f / 3.0f))));
}
/** Draws a circle using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
public void Circle(float x, float y, float radius, int segments) {
if (segments <= 0) throw new ArgumentException("segments must be > 0.");
float angle = 2 * MathUtils.PI / segments;
float cos = MathUtils.Cos(angle);
float sin = MathUtils.Sin(angle);
float cx = radius, cy = 0;
float temp = 0;
for (int i = 0; i < segments; i++) {
vertices.Add(new VertexPositionColor(new Vector3(x + cx, y + cy, 0), color));
temp = cx;
cx = cos * cx - sin * cy;
cy = sin * temp + cos * cy;
vertices.Add(new VertexPositionColor(new Vector3(x + cx, y + cy, 0), color));
}
vertices.Add(new VertexPositionColor(new Vector3(x + cx, y + cy, 0), color));
temp = cx;
cx = radius;
cy = 0;
vertices.Add(new VertexPositionColor(new Vector3(x + cx, y + cy, 0), color));
}
public void Triangle(float x1, float y1, float x2, float y2, float x3, float y3) {
vertices.Add(new VertexPositionColor(new Vector3(x1, y1, 0), color));
vertices.Add(new VertexPositionColor(new Vector3(x2, y2, 0), color));
vertices.Add(new VertexPositionColor(new Vector3(x2, y2, 0), color));
vertices.Add(new VertexPositionColor(new Vector3(x3, y3, 0), color));
vertices.Add(new VertexPositionColor(new Vector3(x3, y3, 0), color));
vertices.Add(new VertexPositionColor(new Vector3(x1, y1, 0), color));
}
public void X(float x, float y, float len) {
Line(x + len, y + len, x - len, y - len);
Line(x - len, y + len, x + len, y - len);
}
public void Polygon(float[] polygonVertices, int offset, int count) {
if (count< 3) throw new ArgumentException("Polygon must contain at least 3 vertices");
offset <<= 1;
count <<= 1;
var firstX = polygonVertices[offset];
var firstY = polygonVertices[offset + 1];
var last = offset + count;
for (int i = offset, n = offset + count - 2; i<n; i += 2) {
var x1 = polygonVertices[i];
var y1 = polygonVertices[i + 1];
var x2 = 0f;
var y2 = 0f;
if (i + 2 >= last) {
x2 = firstX;
y2 = firstY;
} else {
x2 = polygonVertices[i + 2];
y2 = polygonVertices[i + 3];
}
Line(x1, y1, x2, y2);
}
}
public void Rect(float x, float y, float width, float height) {
Line(x, y, x + width, y);
Line(x + width, y, x + width, y + height);
Line(x + width, y + height, x, y + height);
Line(x, y + height, x, y);
}
public void End() {
if (vertices.Count == 0) return;
var verticesArray = vertices.ToArray();
foreach (EffectPass pass in effect.CurrentTechnique.Passes) {
pass.Apply();
device.DrawUserPrimitives(PrimitiveType.LineList, verticesArray, 0, verticesArray.Length / 2);
}
vertices.Clear();
}
}
}

View File

@ -0,0 +1,230 @@
/******************************************************************************
* 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 Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Spine {
public class SkeletonDebugRenderer {
ShapeRenderer renderer;
private static Color boneLineColor = new Color(1f, 0f, 0f, 1f);
private static Color boneOriginColor = new Color(0f, 1f, 0f, 1f);
private static Color attachmentLineColor = new Color(0f, 0f, 1f, 0.5f);
private static Color triangleLineColor = new Color(1f, 0.64f, 0f, 0.5f);
private static Color pathColor = new Color(1f, 0.5f, 0f, 1f);
private static Color clipColor = new Color(0.8f, 0f, 0f, 1f);
private static Color clipDecomposedColor = new Color(0.8f, 0.8f, 0f, 1f);
private static Color aabbColor = new Color(0f, 1f, 0f, 0.5f);
public BasicEffect Effect { get { return renderer.Effect; } set { renderer.Effect = value; } }
public bool DrawBones { get; set; }
public bool DrawRegionAttachments { get; set; }
public bool DrawBoundingBoxes { get; set; }
public bool DrawMeshHull { get; set; }
public bool DrawMeshTriangles { get; set; }
public bool DrawPaths { get; set; }
public bool DrawClipping { get; set; }
public bool DrawClippingDecomposed { get; set; }
public bool DrawSkeletonXY { get; set; }
public void DisableAll() {
DrawBones = false;
DrawRegionAttachments = false;
DrawBoundingBoxes = false;
DrawMeshHull = false;
DrawMeshTriangles = false;
DrawPaths = false;
DrawClipping = false;
DrawSkeletonXY = false;
}
public void EnableAll() {
DrawBones = true;
DrawRegionAttachments = true;
DrawBoundingBoxes = true;
DrawMeshHull = true;
DrawMeshTriangles = true;
DrawPaths = true;
DrawClipping = true;
DrawSkeletonXY = true;
}
private float[] vertices = new float[1024 * 2];
private SkeletonBounds bounds = new SkeletonBounds();
private Triangulator triangulator = new Triangulator();
public SkeletonDebugRenderer (GraphicsDevice device) {
renderer = new ShapeRenderer(device);
EnableAll();
}
public void Begin() {
renderer.Begin();
}
public void Draw(Skeleton skeleton) {
var skeletonX = skeleton.X;
var skeletonY = skeleton.Y;
var bones = skeleton.Bones;
if (DrawBones) {
renderer.SetColor(boneLineColor);
for (int i = 0, n = bones.Count; i < n; i++) {
var bone = bones.Items[i];
if (bone.Parent == null) continue;
var x = bone.Data.Length * bone.A + bone.WorldX;
var y = bone.Data.Length * bone.C + bone.WorldY;
renderer.Line(bone.WorldX, bone.WorldY, x, y);
}
if (DrawSkeletonXY) renderer.X(skeletonX, skeletonY, 4);
}
if (DrawRegionAttachments) {
renderer.SetColor(attachmentLineColor);
var slots = skeleton.Slots;
for (int i = 0, n = slots.Count; i < n; i++) {
var slot = slots.Items[i];
var attachment = slot.Attachment;
if (attachment is RegionAttachment) {
var regionAttachment = (RegionAttachment) attachment;
var vertices = this.vertices;
regionAttachment.ComputeWorldVertices(slot.Bone, vertices, 0, 2);
renderer.Line(vertices[0], vertices[1], vertices[2], vertices[3]);
renderer.Line(vertices[2], vertices[3], vertices[4], vertices[5]);
renderer.Line(vertices[4], vertices[5], vertices[6], vertices[7]);
renderer.Line(vertices[6], vertices[7], vertices[0], vertices[1]);
}
}
}
if (DrawMeshHull || DrawMeshTriangles) {
var slots = skeleton.Slots;
for (int i = 0, n = slots.Count; i < n; i++) {
var slot = slots.Items[i];
var attachment = slot.Attachment;
if (!(attachment is MeshAttachment)) continue;
var mesh = (MeshAttachment)attachment;
var world = vertices = vertices.Length < mesh.WorldVerticesLength ? new float[mesh.WorldVerticesLength] : vertices;
mesh.ComputeWorldVertices(slot, 0, mesh.WorldVerticesLength, world, 0, 2);
int[] triangles = mesh.Triangles;
var hullLength = mesh.HullLength;
if (DrawMeshTriangles) {
renderer.SetColor(triangleLineColor);
for (int ii = 0, nn = triangles.Count(); ii < nn; ii += 3) {
int v1 = triangles[ii] * 2, v2 = triangles[ii + 1] * 2, v3 = triangles[ii + 2] * 2;
renderer.Triangle(world[v1], world[v1 + 1], //
world[v2], world[v2 + 1], //
world[v3], world[v3 + 1] //
);
}
}
if (DrawMeshHull && hullLength > 0) {
renderer.SetColor(attachmentLineColor);
hullLength = (hullLength >> 1) * 2;
float lastX = vertices[hullLength - 2], lastY = vertices[hullLength - 1];
for (int ii = 0, nn = hullLength; ii < nn; ii += 2) {
float x = vertices[ii], y = vertices[ii + 1];
renderer.Line(x, y, lastX, lastY);
lastX = x;
lastY = y;
}
}
}
}
if (DrawBoundingBoxes) {
var bounds = this.bounds;
bounds.Update(skeleton, true);
renderer.SetColor(aabbColor);
renderer.Rect(bounds.MinX, bounds.MinY, bounds.Width, bounds.Height);
var polygons = bounds.Polygons;
var boxes = bounds.BoundingBoxes;
for (int i = 0, n = polygons.Count; i < n; i++) {
var polygon = polygons.Items[i];
renderer.Polygon(polygon.Vertices, 0, polygon.Count);
}
}
if (DrawBones) {
renderer.SetColor(boneOriginColor);
for (int i = 0, n = bones.Count; i < n; i++) {
var bone = bones.Items[i];
renderer.Circle(bone.WorldX, bone.WorldY, 3);
}
}
if (DrawClipping) {
var slots = skeleton.Slots;
renderer.SetColor(clipColor);
for (int i = 0, n = slots.Count; i < n; i++) {
var slot = slots.Items[i];
var attachment = slot.Attachment;
if (!(attachment is ClippingAttachment)) continue;
var clip = (ClippingAttachment)attachment;
var nn = clip.WorldVerticesLength;
var world = vertices = vertices.Length < nn ? new float[nn] : vertices;
clip.ComputeWorldVertices(slot, 0, nn, world, 0, 2);
ExposedList<float> clippingPolygon = new ExposedList<float>();
for (int ii = 0; ii < nn; ii += 2) {
var x = world[ii];
var y = world[ii + 1];
var x2 = world[(ii + 2) % nn];
var y2 = world[(ii + 3) % nn];
renderer.Line(x, y, x2, y2);
clippingPolygon.Add(x);
clippingPolygon.Add(y);
}
if (DrawClippingDecomposed) {
SkeletonClipping.MakeClockwise(clippingPolygon);
var triangles = triangulator.Triangulate(clippingPolygon);
var clippingPolygons = triangulator.Decompose(clippingPolygon, triangles);
renderer.SetColor(clipDecomposedColor);
foreach (var polygon in clippingPolygons) {
SkeletonClipping.MakeClockwise(polygon);
polygon.Add(polygon.Items[0]);
polygon.Add(polygon.Items[1]);
renderer.Polygon(polygon.Items, 0, polygon.Count >> 1);
}
}
}
}
}
public void End() {
renderer.End();
}
}
}