diff --git a/.gitignore b/.gitignore index cd1357110..083184f9c 100644 --- a/.gitignore +++ b/.gitignore @@ -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.* diff --git a/CHANGELOG.md b/CHANGELOG.md index 8525ae15f..539cb7a54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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("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** diff --git a/spine-csharp/src/Atlas.cs b/spine-csharp/src/Atlas.cs index 7701c1831..04f2c5755 100644 --- a/spine-csharp/src/Atlas.cs +++ b/spine-csharp/src/Atlas.cs @@ -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 regions = new List(); 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); diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index cb3629c2e..bbb17efdc 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -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 diff --git a/spine-csharp/src/SkeletonClipping.cs b/spine-csharp/src/SkeletonClipping.cs index 8d5857a9a..1dcbb0a0e 100644 --- a/spine-csharp/src/SkeletonClipping.cs +++ b/spine-csharp/src/SkeletonClipping.cs @@ -154,7 +154,7 @@ namespace Spine { clippedUVsItems[s + 2] = u2; clippedUVsItems[s + 3] = v2; clippedUVsItems[s + 4] = u3; - clippedUVsItems[s + 5] = v3; + clippedUVsItems[s + 5] = v3; s = clippedTriangles.Count; int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3).Items; @@ -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 polygon) { + public static void MakeClockwise (ExposedList polygon) { float[] vertices = polygon.Items; int verticeslength = polygon.Count; diff --git a/spine-csharp/src/Triangulator.cs b/spine-csharp/src/Triangulator.cs index d19a13b39..a4083c3ec 100644 --- a/spine-csharp/src/Triangulator.cs +++ b/spine-csharp/src/Triangulator.cs @@ -31,7 +31,7 @@ using System; namespace Spine { - internal class Triangulator { + public class Triangulator { private readonly ExposedList> convexPolygons = new ExposedList>(); private readonly ExposedList> convexPolygonsIndices = new ExposedList>(); @@ -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]); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java index ebe38e0d6..7c3a12959 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java @@ -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]); diff --git a/spine-ue4/README.md b/spine-ue4/README.md index d91730ec3..c292d028a 100644 --- a/spine-ue4/README.md +++ b/spine-ue4/README.md @@ -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. diff --git a/spine-unity/Assets/Examples/Other Examples/Instantiate from Script.unity b/spine-unity/Assets/Examples/Other Examples/Instantiate from Script.unity new file mode 100644 index 000000000..aa6afdda8 --- /dev/null +++ b/spine-unity/Assets/Examples/Other Examples/Instantiate from Script.unity @@ -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} diff --git a/spine-unity/Assets/Examples/Other Examples/Instantiate from Script.unity.meta b/spine-unity/Assets/Examples/Other Examples/Instantiate from Script.unity.meta new file mode 100644 index 000000000..c684a77a8 --- /dev/null +++ b/spine-unity/Assets/Examples/Other Examples/Instantiate from Script.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 59dc5776e19e2f041b1cac961a86924f +timeCreated: 1500249265 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/Examples/Scripts/DataAssetsFromExportsExample.cs b/spine-unity/Assets/Examples/Scripts/DataAssetsFromExportsExample.cs new file mode 100644 index 000000000..c25994a4c --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/DataAssetsFromExportsExample.cs @@ -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().sortingOrder = 10; + runtimeSkeletonAnimation.transform.Translate(Vector3.down * 2); + + } + } + +} diff --git a/spine-unity/Assets/Examples/Scripts/DataAssetsFromExportsExample.cs.meta b/spine-unity/Assets/Examples/Scripts/DataAssetsFromExportsExample.cs.meta new file mode 100644 index 000000000..680fe1127 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/DataAssetsFromExportsExample.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bb0837af7345d504db63d0c662fd12dc +timeCreated: 1500249349 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/Examples/Scripts/MixAndMatch.cs b/spine-unity/Assets/Examples/Scripts/MixAndMatch.cs index 9f0e69ea9..05a1e81e3 100644 --- a/spine-unity/Assets/Examples/Scripts/MixAndMatch.cs +++ b/spine-unity/Assets/Examples/Scripts/MixAndMatch.cs @@ -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(); } diff --git a/spine-unity/Assets/Examples/Scripts/SpawnFromSkeletonDataExample.cs b/spine-unity/Assets/Examples/Scripts/SpawnFromSkeletonDataExample.cs new file mode 100644 index 000000000..49a57a355 --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/SpawnFromSkeletonDataExample.cs @@ -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); + } + } + + } + +} diff --git a/spine-unity/Assets/Examples/Scripts/SpawnFromSkeletonDataExample.cs.meta b/spine-unity/Assets/Examples/Scripts/SpawnFromSkeletonDataExample.cs.meta new file mode 100644 index 000000000..2bcff9bec --- /dev/null +++ b/spine-unity/Assets/Examples/Scripts/SpawnFromSkeletonDataExample.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7193e2e00836b124191dcae19e6c9741 +timeCreated: 1500249330 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/Examples/Spine/Dragon/dragon_SkeletonData.asset b/spine-unity/Assets/Examples/Spine/Dragon/dragon_SkeletonData.asset index 74028988b..5c16dab6d 100644 --- a/spine-unity/Assets/Examples/Spine/Dragon/dragon_SkeletonData.asset +++ b/spine-unity/Assets/Examples/Spine/Dragon/dragon_SkeletonData.asset @@ -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: [] diff --git a/spine-unity/Assets/Examples/Spine/Runtime Template Material.mat b/spine-unity/Assets/Examples/Spine/Runtime Template Material.mat new file mode 100644 index 000000000..4738491da --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/Runtime Template Material.mat @@ -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} diff --git a/spine-unity/Assets/Examples/Spine/Runtime Template Material.mat.meta b/spine-unity/Assets/Examples/Spine/Runtime Template Material.mat.meta new file mode 100644 index 000000000..c8e787cf9 --- /dev/null +++ b/spine-unity/Assets/Examples/Spine/Runtime Template Material.mat.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 365eb017d7ae7134d820c8b808eeb121 +timeCreated: 1500250447 +licenseType: Free +NativeFormatImporter: + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs b/spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs index 5eb5b87f1..80bc8f5e6 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/AtlasAsset.cs @@ -60,10 +60,7 @@ namespace Spine.Unity { /// /// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches. - 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 { /// /// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches. - 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; } diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index 52a414894..9f4cb07ba 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -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); diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs b/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs index f910885af..16060da32 100644 --- a/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs +++ b/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs @@ -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) { diff --git a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs index 466059fdb..163e1ba52 100644 --- a/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs +++ b/spine-unity/Assets/spine-unity/Modules/AttachmentTools/AttachmentTools.cs @@ -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 CachedRegionTextures = new Dictionary(); + static List CachedRegionTexturesList = new List(); + + public static void ClearCache () { + foreach (var t in CachedRegionTexturesList) { + UnityEngine.Object.Destroy(t); + } + CachedRegionTextures.Clear(); + CachedRegionTexturesList.Clear(); + } + /// 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. public static Texture2D ToTexture (this AtlasRegion ar, bool applyImmediately = true, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { - 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.name = ar.name; - Color[] pixelBuffer = sourceTexture.GetPixels((int)r.x, (int)r.y, width, height); - output.SetPixels(pixelBuffer); + Texture2D output; - if (applyImmediately) - output.Apply(); + 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; + 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; } diff --git a/spine-xna/example/spine-xna-example.csproj b/spine-xna/example/spine-xna-example.csproj index 87edb92d3..1ff220dd9 100644 --- a/spine-xna/example/spine-xna-example.csproj +++ b/spine-xna/example/spine-xna-example.csproj @@ -122,6 +122,9 @@ PreserveNewest + + Always + PreserveNewest @@ -195,9 +198,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/spine-xna/example/src/ExampleGame.cs b/spine-xna/example/src/ExampleGame.cs index 825fc3386..aa81a027f 100644 --- a/spine-xna/example/src/ExampleGame.cs +++ b/spine-xna/example/src/ExampleGame.cs @@ -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) { diff --git a/spine-xna/spine-xna.csproj b/spine-xna/spine-xna.csproj index 88bf7f58e..6ae041699 100644 --- a/spine-xna/spine-xna.csproj +++ b/spine-xna/spine-xna.csproj @@ -111,6 +111,8 @@ + + diff --git a/spine-xna/src/ShapeRenderer.cs b/spine-xna/src/ShapeRenderer.cs new file mode 100644 index 000000000..2b6db713c --- /dev/null +++ b/spine-xna/src/ShapeRenderer.cs @@ -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 { + /// + /// Batch drawing of lines and shapes that can be derrived from lines. + /// + /// Call drawing methods in between Begin()/End() + /// + public class ShapeRenderer { + GraphicsDevice device; + List vertices = new List(); + 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= 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(); + } + } +} diff --git a/spine-xna/src/SkeletonDebugRenderer.cs b/spine-xna/src/SkeletonDebugRenderer.cs new file mode 100644 index 000000000..f2aeaa300 --- /dev/null +++ b/spine-xna/src/SkeletonDebugRenderer.cs @@ -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 clippingPolygon = new ExposedList(); + 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(); + } + } +} diff --git a/spine-xna/src/Util.cs b/spine-xna/src/Util.cs index 1df35a44d..cd4f6f0c9 100644 --- a/spine-xna/src/Util.cs +++ b/spine-xna/src/Util.cs @@ -27,8 +27,8 @@ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - + *****************************************************************************/ + using System; using System.IO; using Microsoft.Xna.Framework; @@ -79,4 +79,4 @@ namespace Spine { return Texture2D.FromStream(device, input); } } -} +}