mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
Arrays Mesh Generators now share code.
This commit is contained in:
parent
bf4511c8de
commit
50a0960a7d
@ -87,9 +87,9 @@ public class SkeletonRendererInspector : Editor {
|
||||
String[] skins = new String[component.skeleton.Data.Skins.Count];
|
||||
int skinIndex = 0;
|
||||
for (int i = 0; i < skins.Length; i++) {
|
||||
String name = component.skeleton.Data.Skins.Items[i].Name;
|
||||
skins[i] = name;
|
||||
if (name == initialSkinName.stringValue)
|
||||
String skinNameString = component.skeleton.Data.Skins.Items[i].Name;
|
||||
skins[i] = skinNameString;
|
||||
if (skinNameString == initialSkinName.stringValue)
|
||||
skinIndex = i;
|
||||
}
|
||||
|
||||
@ -121,9 +121,15 @@ public class SkeletonRendererInspector : Editor {
|
||||
const float MaxZSpacing = 0f;
|
||||
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
|
||||
|
||||
EditorGUILayout.PropertyField(normals);
|
||||
EditorGUILayout.PropertyField(tangents);
|
||||
EditorGUILayout.PropertyField(front);
|
||||
if (normals != null) {
|
||||
EditorGUILayout.PropertyField(normals);
|
||||
EditorGUILayout.PropertyField(tangents);
|
||||
}
|
||||
|
||||
if (front != null) {
|
||||
EditorGUILayout.PropertyField(front);
|
||||
}
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
@ -1,149 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
public static class ArraysBuffers {
|
||||
|
||||
public static void Fill (Skeleton skeleton, int startSlot, int endSlot, float zSpacing, bool pmaColors, Vector3[] vertices, Vector2[] uvs, Color32[] colors32, ref int vertexIndex, ref float[] attVertBuffer, ref Vector3 meshBoundsMin, ref Vector3 meshBoundsMax) {
|
||||
Color32 color;
|
||||
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
|
||||
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
|
||||
|
||||
// drawOrder[endSlot] is excluded
|
||||
for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
|
||||
var slot = skeletonDrawOrderItems[slotIndex];
|
||||
var attachment = slot.attachment;
|
||||
float z = slotIndex * zSpacing;
|
||||
|
||||
var regionAttachment = attachment as RegionAttachment;
|
||||
if (regionAttachment != null) {
|
||||
regionAttachment.ComputeWorldVertices(slot.bone, attVertBuffer);
|
||||
|
||||
float x1 = attVertBuffer[RegionAttachment.X1], y1 = attVertBuffer[RegionAttachment.Y1];
|
||||
float x2 = attVertBuffer[RegionAttachment.X2], y2 = attVertBuffer[RegionAttachment.Y2];
|
||||
float x3 = attVertBuffer[RegionAttachment.X3], y3 = attVertBuffer[RegionAttachment.Y3];
|
||||
float x4 = attVertBuffer[RegionAttachment.X4], y4 = attVertBuffer[RegionAttachment.Y4];
|
||||
vertices[vertexIndex].x = x1; vertices[vertexIndex].y = y1; vertices[vertexIndex].z = z;
|
||||
vertices[vertexIndex + 1].x = x4; vertices[vertexIndex + 1].y = y4; vertices[vertexIndex + 1].z = z;
|
||||
vertices[vertexIndex + 2].x = x2; vertices[vertexIndex + 2].y = y2; vertices[vertexIndex + 2].z = z;
|
||||
vertices[vertexIndex + 3].x = x3; vertices[vertexIndex + 3].y = y3; vertices[vertexIndex + 3].z = z;
|
||||
|
||||
if (pmaColors) {
|
||||
color.a = (byte)(a * slot.a * regionAttachment.a);
|
||||
color.r = (byte)(r * slot.r * regionAttachment.r * color.a);
|
||||
color.g = (byte)(g * slot.g * regionAttachment.g * color.a);
|
||||
color.b = (byte)(b * slot.b * regionAttachment.b * color.a);
|
||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
} else {
|
||||
color.a = (byte)(a * slot.a * regionAttachment.a);
|
||||
color.r = (byte)(r * slot.r * regionAttachment.r * 255);
|
||||
color.g = (byte)(g * slot.g * regionAttachment.g * 255);
|
||||
color.b = (byte)(b * slot.b * regionAttachment.b * 255);
|
||||
}
|
||||
|
||||
colors32[vertexIndex] = color; colors32[vertexIndex + 1] = color; colors32[vertexIndex + 2] = color; colors32[vertexIndex + 3] = color;
|
||||
|
||||
float[] regionUVs = regionAttachment.uvs;
|
||||
uvs[vertexIndex].x = regionUVs[RegionAttachment.X1]; uvs[vertexIndex].y = regionUVs[RegionAttachment.Y1];
|
||||
uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4]; uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4];
|
||||
uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2]; uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2];
|
||||
uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3]; uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3];
|
||||
|
||||
// Calculate min/max X
|
||||
if (x1 < meshBoundsMin.x) meshBoundsMin.x = x1;
|
||||
else if (x1 > meshBoundsMax.x) meshBoundsMax.x = x1;
|
||||
if (x2 < meshBoundsMin.x) meshBoundsMin.x = x2;
|
||||
else if (x2 > meshBoundsMax.x) meshBoundsMax.x = x2;
|
||||
if (x3 < meshBoundsMin.x) meshBoundsMin.x = x3;
|
||||
else if (x3 > meshBoundsMax.x) meshBoundsMax.x = x3;
|
||||
if (x4 < meshBoundsMin.x) meshBoundsMin.x = x4;
|
||||
else if (x4 > meshBoundsMax.x) meshBoundsMax.x = x4;
|
||||
|
||||
// Calculate min/max Y
|
||||
if (y1 < meshBoundsMin.y) meshBoundsMin.y = y1;
|
||||
else if (y1 > meshBoundsMax.y) meshBoundsMax.y = y1;
|
||||
if (y2 < meshBoundsMin.y) meshBoundsMin.y = y2;
|
||||
else if (y2 > meshBoundsMax.y) meshBoundsMax.y = y2;
|
||||
if (y3 < meshBoundsMin.y) meshBoundsMin.y = y3;
|
||||
else if (y3 > meshBoundsMax.y) meshBoundsMax.y = y3;
|
||||
if (y4 < meshBoundsMin.y) meshBoundsMin.y = y4;
|
||||
else if (y4 > meshBoundsMax.y) meshBoundsMax.y = y4;
|
||||
|
||||
vertexIndex += 4;
|
||||
} else {
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
int meshVertexCount = meshAttachment.vertices.Length;
|
||||
if (attVertBuffer.Length < meshVertexCount) attVertBuffer = new float[meshVertexCount];
|
||||
meshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
||||
|
||||
if (pmaColors) {
|
||||
color.a = (byte)(a * slot.a * meshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * meshAttachment.r * color.a);
|
||||
color.g = (byte)(g * slot.g * meshAttachment.g * color.a);
|
||||
color.b = (byte)(b * slot.b * meshAttachment.b * color.a);
|
||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
} else {
|
||||
color.a = (byte)(a * slot.a * meshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * meshAttachment.r * 255);
|
||||
color.g = (byte)(g * slot.g * meshAttachment.g * 255);
|
||||
color.b = (byte)(b * slot.b * meshAttachment.b * 255);
|
||||
}
|
||||
|
||||
float[] attachmentUVs = meshAttachment.uvs;
|
||||
for (int iii = 0; iii < meshVertexCount; iii += 2) {
|
||||
float x = attVertBuffer[iii], y = attVertBuffer[iii + 1];
|
||||
vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z;
|
||||
colors32[vertexIndex] = color; uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 1];
|
||||
|
||||
if (x < meshBoundsMin.x) meshBoundsMin.x = x;
|
||||
else if (x > meshBoundsMax.x) meshBoundsMax.x = x;
|
||||
|
||||
if (y < meshBoundsMin.y) meshBoundsMin.y = y;
|
||||
else if (y > meshBoundsMax.y) meshBoundsMax.y = y;
|
||||
|
||||
vertexIndex++;
|
||||
}
|
||||
} else {
|
||||
var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (weightedMeshAttachment != null) {
|
||||
int meshVertexCount = weightedMeshAttachment.uvs.Length;
|
||||
if (attVertBuffer.Length < meshVertexCount) attVertBuffer = new float[meshVertexCount];
|
||||
weightedMeshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
||||
|
||||
if (pmaColors) {
|
||||
color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a);
|
||||
color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a);
|
||||
color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a);
|
||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
} else {
|
||||
color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * weightedMeshAttachment.r * 255);
|
||||
color.g = (byte)(g * slot.g * weightedMeshAttachment.g * 255);
|
||||
color.b = (byte)(b * slot.b * weightedMeshAttachment.b * 255);
|
||||
}
|
||||
|
||||
float[] attachmentUVs = weightedMeshAttachment.uvs;
|
||||
for (int iii = 0; iii < meshVertexCount; iii += 2) {
|
||||
float x = attVertBuffer[iii], y = attVertBuffer[iii + 1];
|
||||
vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z;
|
||||
colors32[vertexIndex] = color;
|
||||
uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 1];
|
||||
|
||||
if (x < meshBoundsMin.x) meshBoundsMin.x = x;
|
||||
else if (x > meshBoundsMax.x) meshBoundsMax.x = x;
|
||||
if (y < meshBoundsMin.y) meshBoundsMin.y = y;
|
||||
else if (y > meshBoundsMax.y) meshBoundsMax.y = y;
|
||||
|
||||
vertexIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Fill(...)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 043e864df13214e4989d29c17a863b08
|
||||
timeCreated: 1458056200
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,327 @@
|
||||
/******************************************************************************
|
||||
* 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 UnityEngine;
|
||||
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
public class ArraysMeshGenerator {
|
||||
#region Settings
|
||||
protected bool premultiplyVertexColors = true;
|
||||
public bool PremultiplyVertexColors { get { return this.premultiplyVertexColors; } set { this.premultiplyVertexColors = value; } }
|
||||
#endregion
|
||||
|
||||
protected float[] attachmentVertexBuffer = new float[8];
|
||||
protected Vector3[] meshVertices;
|
||||
protected Color32[] meshColors32;
|
||||
protected Vector2[] meshUVs;
|
||||
|
||||
public static bool EnsureSize (int targetVertexCount, ref Vector3[] vertices, ref Vector2[] uvs, ref Color32[] colors) {
|
||||
Vector3[] verts = vertices;
|
||||
bool verticesWasResized = verts == null || targetVertexCount > verts.Length;
|
||||
if (verticesWasResized) {
|
||||
// Not enough space, increase size.
|
||||
vertices = new Vector3[targetVertexCount];
|
||||
colors = new Color32[targetVertexCount];
|
||||
uvs = new Vector2[targetVertexCount];
|
||||
} else {
|
||||
// Too many vertices, zero the extra.
|
||||
Vector3 zero = Vector3.zero;
|
||||
for (int i = targetVertexCount, n = verts.Length; i < n; i++)
|
||||
verts[i] = zero;
|
||||
}
|
||||
return verticesWasResized;
|
||||
}
|
||||
|
||||
public static bool EnsureTriangleBuffersSize (ExposedList<SubmeshTriangleBuffer> submeshBuffers, int targetSubmeshCount, SubmeshInstruction[] instructionItems) {
|
||||
bool submeshBuffersWasResized = submeshBuffers.Count < targetSubmeshCount;
|
||||
if (submeshBuffersWasResized) {
|
||||
submeshBuffers.GrowIfNeeded(targetSubmeshCount - submeshBuffers.Count);
|
||||
for (int i = submeshBuffers.Count; submeshBuffers.Count < targetSubmeshCount; i++)
|
||||
submeshBuffers.Add(new SubmeshTriangleBuffer(instructionItems[i].triangleCount));
|
||||
}
|
||||
return submeshBuffersWasResized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills vertex arrays.
|
||||
/// </summary>
|
||||
/// <param name="skeleton">Spine.Skeleton source of the drawOrder array</param>
|
||||
/// <param name="startSlot">Slot index of the first slot.</param>
|
||||
/// <param name="endSlot">The index bounding the slot list. endSlot - 1 is the last slot to be added.</param>
|
||||
/// <param name="zSpacing">Spacing along the z-axis between attachments.</param>
|
||||
/// <param name="pmaColors">If set to <c>true</c>, vertex colors will be premultiplied. This will also enable additive.</param>
|
||||
/// <param name="verts">Vertex positions array. </param>
|
||||
/// <param name="uvs">Vertex UV array.</param>
|
||||
/// <param name="colors">Vertex color array (Color32).</param>
|
||||
/// <param name="vertexIndex">A reference to the running vertex index. This is used when more than one submesh is to be added.</param>
|
||||
/// <param name="tempVertBuffer">A temporary vertex position buffer for attachment position values.</param>
|
||||
/// <param name="boundsMin">Reference to the running calculated minimum bounds.</param>
|
||||
/// <param name="boundsMax">Reference to the running calculated maximum bounds.</param>
|
||||
public static void FillVerts (Skeleton skeleton, int startSlot, int endSlot, float zSpacing, bool pmaColors, Vector3[] verts, Vector2[] uvs, Color32[] colors, ref int vertexIndex, ref float[] tempVertBuffer, ref Vector3 boundsMin, ref Vector3 boundsMax) {
|
||||
Color32 color;
|
||||
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
|
||||
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
|
||||
|
||||
int vi = vertexIndex;
|
||||
var tempVerts = tempVertBuffer;
|
||||
Vector3 bmin = boundsMin;
|
||||
Vector3 bmax = boundsMax;
|
||||
|
||||
// drawOrder[endSlot] is excluded
|
||||
for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
|
||||
var slot = skeletonDrawOrderItems[slotIndex];
|
||||
var attachment = slot.attachment;
|
||||
float z = slotIndex * zSpacing;
|
||||
|
||||
var regionAttachment = attachment as RegionAttachment;
|
||||
if (regionAttachment != null) {
|
||||
regionAttachment.ComputeWorldVertices(slot.bone, tempVerts);
|
||||
|
||||
float x1 = tempVerts[RegionAttachment.X1], y1 = tempVerts[RegionAttachment.Y1];
|
||||
float x2 = tempVerts[RegionAttachment.X2], y2 = tempVerts[RegionAttachment.Y2];
|
||||
float x3 = tempVerts[RegionAttachment.X3], y3 = tempVerts[RegionAttachment.Y3];
|
||||
float x4 = tempVerts[RegionAttachment.X4], y4 = tempVerts[RegionAttachment.Y4];
|
||||
verts[vi].x = x1; verts[vi].y = y1; verts[vi].z = z;
|
||||
verts[vi + 1].x = x4; verts[vi + 1].y = y4; verts[vi + 1].z = z;
|
||||
verts[vi + 2].x = x2; verts[vi + 2].y = y2; verts[vi + 2].z = z;
|
||||
verts[vi + 3].x = x3; verts[vi + 3].y = y3; verts[vi + 3].z = z;
|
||||
|
||||
if (pmaColors) {
|
||||
color.a = (byte)(a * slot.a * regionAttachment.a);
|
||||
color.r = (byte)(r * slot.r * regionAttachment.r * color.a);
|
||||
color.g = (byte)(g * slot.g * regionAttachment.g * color.a);
|
||||
color.b = (byte)(b * slot.b * regionAttachment.b * color.a);
|
||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
} else {
|
||||
color.a = (byte)(a * slot.a * regionAttachment.a);
|
||||
color.r = (byte)(r * slot.r * regionAttachment.r * 255);
|
||||
color.g = (byte)(g * slot.g * regionAttachment.g * 255);
|
||||
color.b = (byte)(b * slot.b * regionAttachment.b * 255);
|
||||
}
|
||||
|
||||
colors[vi] = color; colors[vi + 1] = color; colors[vi + 2] = color; colors[vi + 3] = color;
|
||||
|
||||
float[] regionUVs = regionAttachment.uvs;
|
||||
uvs[vi].x = regionUVs[RegionAttachment.X1]; uvs[vi].y = regionUVs[RegionAttachment.Y1];
|
||||
uvs[vi + 1].x = regionUVs[RegionAttachment.X4]; uvs[vi + 1].y = regionUVs[RegionAttachment.Y4];
|
||||
uvs[vi + 2].x = regionUVs[RegionAttachment.X2]; uvs[vi + 2].y = regionUVs[RegionAttachment.Y2];
|
||||
uvs[vi + 3].x = regionUVs[RegionAttachment.X3]; uvs[vi + 3].y = regionUVs[RegionAttachment.Y3];
|
||||
|
||||
// Calculate min/max X
|
||||
if (x1 < bmin.x) bmin.x = x1;
|
||||
else if (x1 > bmax.x) bmax.x = x1;
|
||||
if (x2 < bmin.x) bmin.x = x2;
|
||||
else if (x2 > bmax.x) bmax.x = x2;
|
||||
if (x3 < bmin.x) bmin.x = x3;
|
||||
else if (x3 > bmax.x) bmax.x = x3;
|
||||
if (x4 < bmin.x) bmin.x = x4;
|
||||
else if (x4 > bmax.x) bmax.x = x4;
|
||||
|
||||
// Calculate min/max Y
|
||||
if (y1 < bmin.y) bmin.y = y1;
|
||||
else if (y1 > bmax.y) bmax.y = y1;
|
||||
if (y2 < bmin.y) bmin.y = y2;
|
||||
else if (y2 > bmax.y) bmax.y = y2;
|
||||
if (y3 < bmin.y) bmin.y = y3;
|
||||
else if (y3 > bmax.y) bmax.y = y3;
|
||||
if (y4 < bmin.y) bmin.y = y4;
|
||||
else if (y4 > bmax.y) bmax.y = y4;
|
||||
|
||||
vi += 4;
|
||||
} else {
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
int meshVertexCount = meshAttachment.vertices.Length;
|
||||
if (tempVerts.Length < meshVertexCount) tempVerts = new float[meshVertexCount];
|
||||
meshAttachment.ComputeWorldVertices(slot, tempVerts);
|
||||
|
||||
if (pmaColors) {
|
||||
color.a = (byte)(a * slot.a * meshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * meshAttachment.r * color.a);
|
||||
color.g = (byte)(g * slot.g * meshAttachment.g * color.a);
|
||||
color.b = (byte)(b * slot.b * meshAttachment.b * color.a);
|
||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
} else {
|
||||
color.a = (byte)(a * slot.a * meshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * meshAttachment.r * 255);
|
||||
color.g = (byte)(g * slot.g * meshAttachment.g * 255);
|
||||
color.b = (byte)(b * slot.b * meshAttachment.b * 255);
|
||||
}
|
||||
|
||||
float[] attachmentUVs = meshAttachment.uvs;
|
||||
for (int iii = 0; iii < meshVertexCount; iii += 2) {
|
||||
float x = tempVerts[iii], y = tempVerts[iii + 1];
|
||||
verts[vi].x = x; verts[vi].y = y; verts[vi].z = z;
|
||||
colors[vi] = color; uvs[vi].x = attachmentUVs[iii]; uvs[vi].y = attachmentUVs[iii + 1];
|
||||
|
||||
if (x < bmin.x) bmin.x = x;
|
||||
else if (x > bmax.x) bmax.x = x;
|
||||
|
||||
if (y < bmin.y) bmin.y = y;
|
||||
else if (y > bmax.y) bmax.y = y;
|
||||
|
||||
vi++;
|
||||
}
|
||||
} else {
|
||||
var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (weightedMeshAttachment != null) {
|
||||
int meshVertexCount = weightedMeshAttachment.uvs.Length;
|
||||
if (tempVerts.Length < meshVertexCount) tempVerts = new float[meshVertexCount];
|
||||
weightedMeshAttachment.ComputeWorldVertices(slot, tempVerts);
|
||||
|
||||
if (pmaColors) {
|
||||
color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a);
|
||||
color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a);
|
||||
color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a);
|
||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
} else {
|
||||
color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
|
||||
color.r = (byte)(r * slot.r * weightedMeshAttachment.r * 255);
|
||||
color.g = (byte)(g * slot.g * weightedMeshAttachment.g * 255);
|
||||
color.b = (byte)(b * slot.b * weightedMeshAttachment.b * 255);
|
||||
}
|
||||
|
||||
float[] attachmentUVs = weightedMeshAttachment.uvs;
|
||||
for (int iii = 0; iii < meshVertexCount; iii += 2) {
|
||||
float x = tempVerts[iii], y = tempVerts[iii + 1];
|
||||
verts[vi].x = x; verts[vi].y = y; verts[vi].z = z;
|
||||
colors[vi] = color;
|
||||
uvs[vi].x = attachmentUVs[iii]; uvs[vi].y = attachmentUVs[iii + 1];
|
||||
|
||||
if (x < bmin.x) bmin.x = x;
|
||||
else if (x > bmax.x) bmax.x = x;
|
||||
if (y < bmin.y) bmin.y = y;
|
||||
else if (y > bmax.y) bmax.y = y;
|
||||
|
||||
vi++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ref return values
|
||||
vertexIndex = vi;
|
||||
tempVertBuffer = tempVerts;
|
||||
boundsMin = bmin;
|
||||
boundsMax = bmax;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fills a submesh triangle buffer array.
|
||||
/// </summary>
|
||||
/// <param name="skeleton">Spine.Skeleton source of draw order slots.</param>
|
||||
/// <param name="triangleCount">The target triangle count.</param>
|
||||
/// <param name="firstVertex">First vertex of this submesh.</param>
|
||||
/// <param name="startSlot">Start slot.</param>
|
||||
/// <param name="endSlot">End slot.</param>
|
||||
/// <param name="triangleBuffer">The triangle buffer array to be filled. This reference will be replaced in case the triangle values don't fit.</param>
|
||||
/// <param name="bufferTriangleCount">The current triangle count of the submesh buffer. This is not always equal to triangleBuffer.Length because for last submeshes, length may be larger than needed.</param>
|
||||
/// <param name="isLastSubmesh">If set to <c>true</c>, the triangle buffer is allowed to be larger than needed.</param>
|
||||
public static void FillTriangles (Skeleton skeleton, int triangleCount, int firstVertex, int startSlot, int endSlot, ref int[] triangleBuffer, ref int bufferTriangleCount, bool isLastSubmesh) {
|
||||
int trianglesCapacity = triangleBuffer.Length;
|
||||
var tris = triangleBuffer;
|
||||
|
||||
// Ensure triangleBuffer size.
|
||||
if (isLastSubmesh) {
|
||||
bufferTriangleCount = triangleCount;
|
||||
if (trianglesCapacity > triangleCount) {
|
||||
for (int i = triangleCount; i < trianglesCapacity; i++)
|
||||
tris[i] = 0;
|
||||
} else if (trianglesCapacity < triangleCount) {
|
||||
triangleBuffer = tris = new int[triangleCount];
|
||||
bufferTriangleCount = 0;
|
||||
}
|
||||
} else if (trianglesCapacity != triangleCount) {
|
||||
triangleBuffer = tris = new int[triangleCount];
|
||||
bufferTriangleCount = 0;
|
||||
}
|
||||
|
||||
// Iterate through submesh slots and store the triangles.
|
||||
int triangleIndex = 0;
|
||||
int afv = firstVertex; // attachment first vertex
|
||||
var skeletonDrawOrderItems = skeleton.drawOrder.Items;
|
||||
for (int i = startSlot, n = endSlot; i < n; i++) {
|
||||
var attachment = skeletonDrawOrderItems[i].attachment;
|
||||
|
||||
if (attachment is RegionAttachment) {
|
||||
tris[triangleIndex] = afv; tris[triangleIndex + 1] = afv + 2; tris[triangleIndex + 2] = afv + 1;
|
||||
tris[triangleIndex + 3] = afv + 2; tris[triangleIndex + 4] = afv + 3; tris[triangleIndex + 5] = afv + 1;
|
||||
|
||||
triangleIndex += 6;
|
||||
afv += 4;
|
||||
} else {
|
||||
int[] attachmentTriangles;
|
||||
int attachmentVertexCount;
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2
|
||||
attachmentTriangles = meshAttachment.triangles;
|
||||
} else {
|
||||
var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (weightedMeshAttachment != null) {
|
||||
attachmentVertexCount = weightedMeshAttachment.uvs.Length >> 1; // length/2
|
||||
attachmentTriangles = weightedMeshAttachment.triangles;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
|
||||
tris[triangleIndex] = afv + attachmentTriangles[ii];
|
||||
|
||||
afv += attachmentVertexCount;
|
||||
}
|
||||
} // Done adding current submesh triangles
|
||||
}
|
||||
|
||||
|
||||
public static Bounds ToBounds (Vector3 boundsMin, Vector3 boundsMax) {
|
||||
Vector3 size = (boundsMax - boundsMin);
|
||||
Vector3 center = boundsMin + size * 0.5f;
|
||||
return new Bounds(center, size);
|
||||
}
|
||||
|
||||
#region SubmeshTriangleBuffer
|
||||
public class SubmeshTriangleBuffer {
|
||||
public int[] triangles;
|
||||
public int triangleCount;
|
||||
|
||||
public SubmeshTriangleBuffer (int triangleCount) {
|
||||
triangles = new int[triangleCount];
|
||||
this.triangleCount = triangleCount;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b4bb4dc3775d6548b9405f681601abb
|
||||
timeCreated: 1458056219
|
||||
guid: ed047fa8737dedb4081579983c193db5
|
||||
timeCreated: 1458124926
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
@ -28,42 +28,30 @@
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
public class ArraysSimpleMeshGenerator : ISimpleMeshGenerator {
|
||||
public class ArraysSimpleMeshGenerator : ArraysMeshGenerator, ISimpleMeshGenerator {
|
||||
#region Settings
|
||||
protected float scale = 1f;
|
||||
public float Scale {
|
||||
get { return scale; }
|
||||
set { scale = value; }
|
||||
}
|
||||
|
||||
public bool renderMeshes = true;
|
||||
|
||||
public bool premultiplyVertexColors = true;
|
||||
#endregion
|
||||
|
||||
#region Buffers
|
||||
readonly DoubleBufferedMesh doubleBufferedMesh = new DoubleBufferedMesh();
|
||||
private float[] tempVertices = new float[8];
|
||||
private Vector3[] vertices;
|
||||
private Color32[] colors;
|
||||
private Vector2[] uvs;
|
||||
private int[] triangles;
|
||||
#endregion
|
||||
|
||||
private Mesh lastGeneratedMesh;
|
||||
public Mesh LastGeneratedMesh { get { return lastGeneratedMesh; } }
|
||||
|
||||
readonly DoubleBufferedMesh doubleBufferedMesh = new DoubleBufferedMesh();
|
||||
int[] triangles;
|
||||
int triangleBufferCount;
|
||||
|
||||
public Mesh GenerateMesh (Skeleton skeleton) {
|
||||
int totalVertexCount = 0; // size of vertex arrays
|
||||
int totalTriangleCount = 0; // size of index array
|
||||
|
||||
// Step 1 : Count verts and tris to determine array sizes.
|
||||
//
|
||||
// STEP 1 : GenerateInstruction(). Count verts and tris to determine array sizes.
|
||||
var drawOrderItems = skeleton.drawOrder.Items;
|
||||
int drawOrderCount = skeleton.drawOrder.Count;
|
||||
for (int i = 0; i < drawOrderCount; i++) {
|
||||
@ -75,7 +63,6 @@ namespace Spine.Unity.MeshGeneration {
|
||||
attachmentVertexCount = 4;
|
||||
attachmentTriangleCount = 6;
|
||||
} else {
|
||||
if (!renderMeshes) continue;
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1;
|
||||
@ -93,33 +80,13 @@ namespace Spine.Unity.MeshGeneration {
|
||||
totalVertexCount += attachmentVertexCount;
|
||||
}
|
||||
|
||||
|
||||
// Step 2 : Prepare vertex arrays.
|
||||
//
|
||||
Vector3[] vertices = this.vertices;
|
||||
bool verticesDontFit = vertices == null || totalVertexCount > vertices.Length;
|
||||
if (verticesDontFit) {
|
||||
// Not enough space, increase size.
|
||||
this.vertices = vertices = new Vector3[totalVertexCount];
|
||||
this.colors = new Color32[totalVertexCount];
|
||||
this.uvs = new Vector2[totalVertexCount];
|
||||
|
||||
} else {
|
||||
// Too many vertices, zero the extra.
|
||||
Vector3 zero = Vector3.zero;
|
||||
for (int i = totalVertexCount, n = vertices.Length; i < n; i++)
|
||||
vertices[i] = zero;
|
||||
}
|
||||
// STEP 2 : Ensure buffers are the correct size
|
||||
ArraysMeshGenerator.EnsureSize(totalVertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
|
||||
this.triangles = this.triangles ?? new int[totalTriangleCount];
|
||||
|
||||
|
||||
// Step 3 : Push vertices to arrays
|
||||
//
|
||||
// STEP 3 : Update vertex buffer
|
||||
const float zSpacing = 0;
|
||||
const float zFauxHalfThickness = 0.01f; // Somehow needs this thickness for bounds to work properly in some cases (eg, Unity UI clipping)
|
||||
float[] tempVertices = this.tempVertices;
|
||||
Vector2[] uvs = this.uvs;
|
||||
Color32[] colors = this.colors;
|
||||
|
||||
Vector3 meshBoundsMin;
|
||||
Vector3 meshBoundsMax;
|
||||
if (totalVertexCount == 0) {
|
||||
@ -130,98 +97,34 @@ namespace Spine.Unity.MeshGeneration {
|
||||
meshBoundsMin.y = int.MaxValue;
|
||||
meshBoundsMax.x = int.MinValue;
|
||||
meshBoundsMax.y = int.MinValue;
|
||||
meshBoundsMin.z = -zFauxHalfThickness;
|
||||
meshBoundsMax.z = zFauxHalfThickness;
|
||||
meshBoundsMin.z = -zFauxHalfThickness * scale;
|
||||
meshBoundsMax.z = zFauxHalfThickness * scale;
|
||||
|
||||
int vertexIndex = 0;
|
||||
ArraysBuffers.Fill(skeleton, 0, drawOrderCount, zSpacing, this.premultiplyVertexColors, vertices, uvs, colors, ref vertexIndex, ref tempVertices, ref meshBoundsMin, ref meshBoundsMax);
|
||||
this.tempVertices = tempVertices;
|
||||
ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, zSpacing, this.premultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
|
||||
|
||||
// Apply scale to vertices
|
||||
meshBoundsMax.x *= scale; meshBoundsMax.y *= scale;
|
||||
meshBoundsMin.x *= scale; meshBoundsMax.y *= scale;
|
||||
var vertices = this.meshVertices;
|
||||
for (int i = 0; i < totalVertexCount; i++) {
|
||||
var v = vertices[i];
|
||||
v.x *= scale;
|
||||
v.y *= scale;
|
||||
vertices[i] = v;
|
||||
}
|
||||
|
||||
meshBoundsMax.x *= scale;
|
||||
meshBoundsMax.y *= scale;
|
||||
meshBoundsMin.x *= scale;
|
||||
meshBoundsMax.y *= scale;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Step 3 : Ensure correct triangle array size
|
||||
//
|
||||
var triangles = this.triangles;
|
||||
bool trianglesDontFit = triangles == null || totalTriangleCount > triangles.Length;
|
||||
if (trianglesDontFit) {
|
||||
// Not enough space, increase size
|
||||
this.triangles = triangles = new int[totalTriangleCount];
|
||||
} else {
|
||||
// Too many indices, zero the extra.
|
||||
for (int i = totalTriangleCount, n = triangles.Length; i < n; i++)
|
||||
triangles[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Step 4 : Push triangles to triangle array.
|
||||
//
|
||||
int triangleArrayIndex = 0; // next triangle index. modified by loop
|
||||
int firstAttachmentVertex = 0;
|
||||
for (int i = 0, n = drawOrderCount; i < n; i++) {
|
||||
Attachment attachment = drawOrderItems[i].attachment;
|
||||
|
||||
if (attachment is RegionAttachment) {
|
||||
triangles[triangleArrayIndex] = firstAttachmentVertex;
|
||||
triangles[triangleArrayIndex + 1] = firstAttachmentVertex + 2;
|
||||
triangles[triangleArrayIndex + 2] = firstAttachmentVertex + 1;
|
||||
triangles[triangleArrayIndex + 3] = firstAttachmentVertex + 2;
|
||||
triangles[triangleArrayIndex + 4] = firstAttachmentVertex + 3;
|
||||
triangles[triangleArrayIndex + 5] = firstAttachmentVertex + 1;
|
||||
|
||||
triangleArrayIndex += 6;
|
||||
firstAttachmentVertex += 4;
|
||||
continue;
|
||||
} else {
|
||||
if (!renderMeshes) continue;
|
||||
int[] attachmentTriangles;
|
||||
int attachmentVertexCount;
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2
|
||||
attachmentTriangles = meshAttachment.triangles;
|
||||
} else {
|
||||
var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (skinnedMeshAttachment != null) {
|
||||
attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; // length/2
|
||||
attachmentTriangles = skinnedMeshAttachment.triangles;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleArrayIndex++)
|
||||
triangles[triangleArrayIndex] = firstAttachmentVertex + attachmentTriangles[ii];
|
||||
|
||||
firstAttachmentVertex += attachmentVertexCount;
|
||||
Vector3 p = vertices[i];
|
||||
p.x *= scale;
|
||||
p.y *= scale;
|
||||
vertices[i] = p;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4 : Update Triangles buffer
|
||||
ArraysMeshGenerator.FillTriangles(skeleton, totalTriangleCount, 0, 0, drawOrderCount, ref this.triangles, ref this.triangleBufferCount, true);
|
||||
|
||||
|
||||
// Step 5 : Push Data To Mesh
|
||||
//
|
||||
// Step 5 : Update Mesh with buffers
|
||||
var mesh = doubleBufferedMesh.GetNextMesh();
|
||||
mesh.vertices = vertices;
|
||||
mesh.colors32 = colors;
|
||||
mesh.uv = uvs;
|
||||
|
||||
Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin) * scale;
|
||||
Vector3 meshCenter = (meshBoundsMin * scale) + meshBoundsExtents * 0.5f;
|
||||
mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
|
||||
|
||||
mesh.SetTriangles(triangles, 0);
|
||||
mesh.vertices = this.meshVertices;
|
||||
mesh.colors32 = meshColors32;
|
||||
mesh.uv = meshUVs;
|
||||
mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
|
||||
mesh.triangles = triangles;
|
||||
|
||||
lastGeneratedMesh = mesh;
|
||||
return mesh;
|
||||
|
||||
@ -1,185 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
public class ArraysSingleSubmeshGenerator : ISingleSubmeshGenerator {
|
||||
|
||||
public float zSpacing = 0f;
|
||||
|
||||
bool premultiplyVertexColors = true;
|
||||
public bool PremultiplyVertexColors { get { return this.premultiplyVertexColors; } set { this.premultiplyVertexColors = value; } }
|
||||
|
||||
public Mesh GenerateMesh (SubmeshInstruction instruction) {
|
||||
float zSpacing = this.zSpacing;
|
||||
float[] attVertBuffer = this.attachmentVertexBuffer;
|
||||
Vector2[] uvs = this.meshUVs;
|
||||
Color32[] colors32 = this.meshColors32;
|
||||
var attachmentList = this.attachmentListBuffer;
|
||||
attachmentList.Clear();
|
||||
|
||||
// Ensure correct buffer sizes.
|
||||
Vector3[] vertices = this.meshVertices;
|
||||
|
||||
int instructionVertexCount = instruction.vertexCount;
|
||||
bool newVertices = vertices == null || instructionVertexCount > vertices.Length;
|
||||
if (newVertices) {
|
||||
this.meshVertices = vertices = new Vector3[instructionVertexCount];
|
||||
this.meshColors32 = colors32 = new Color32[instructionVertexCount];
|
||||
this.meshUVs = uvs = new Vector2[instructionVertexCount];
|
||||
} else {
|
||||
var zero = Vector3.zero;
|
||||
for (int i = instructionVertexCount, n = this.meshVertices.Length; i < n; i++)
|
||||
vertices[i] = zero;
|
||||
}
|
||||
|
||||
Vector3 meshBoundsMin;
|
||||
Vector3 meshBoundsMax;
|
||||
|
||||
int attachmentCount = instruction.endSlot - instruction.startSlot;
|
||||
|
||||
// Initial values for manual Mesh Bounds calculation
|
||||
if (attachmentCount <= 0) {
|
||||
meshBoundsMin = new Vector3(0, 0, 0);
|
||||
meshBoundsMax = new Vector3(0, 0, 0);
|
||||
} else {
|
||||
meshBoundsMin.x = int.MaxValue;
|
||||
meshBoundsMin.y = int.MaxValue;
|
||||
meshBoundsMax.x = int.MinValue;
|
||||
meshBoundsMax.y = int.MinValue;
|
||||
|
||||
if (zSpacing > 0f) {
|
||||
meshBoundsMin.z = 0f;
|
||||
meshBoundsMax.z = zSpacing * (attachmentCount - 1);
|
||||
} else {
|
||||
meshBoundsMin.z = zSpacing * (attachmentCount - 1);
|
||||
meshBoundsMax.z = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
var skeleton = instruction.skeleton;
|
||||
int vertexIndex = 0;
|
||||
ArraysBuffers.Fill(skeleton, instruction.startSlot, instruction.endSlot, this.zSpacing, this.premultiplyVertexColors, vertices, uvs, colors32, ref vertexIndex, ref attVertBuffer, ref meshBoundsMin, ref meshBoundsMax);
|
||||
this.attachmentVertexBuffer = attVertBuffer;
|
||||
|
||||
var smartMesh = this.doubleBufferedSmartMesh.GetNext();
|
||||
var mesh = smartMesh.mesh;
|
||||
|
||||
bool structureDoesntMatch = newVertices || smartMesh.StructureDoesntMatch(attachmentList, instruction);
|
||||
|
||||
// Push triangles in this submesh
|
||||
if (structureDoesntMatch) {
|
||||
mesh.Clear();
|
||||
|
||||
int triangleCount = instruction.triangleCount;
|
||||
|
||||
int[] thisTriangles = this.triangles;
|
||||
if (triangles == null || triangles.Length < triangleCount) {
|
||||
this.triangles = thisTriangles = new int[triangleCount];
|
||||
} else if (triangles.Length > triangleCount) {
|
||||
for (int i = triangleCount; i < triangles.Length; i++)
|
||||
thisTriangles[i] = 0;
|
||||
}
|
||||
|
||||
// Iterate through submesh slots and store the triangles.
|
||||
int triangleIndex = 0;
|
||||
int afv = 0; // attachment first vertex, for single submesh, don't use instructions.firstVertexIndex
|
||||
var skeletonDrawOrderItems = skeleton.drawOrder.Items;
|
||||
for (int i = instruction.startSlot, n = instruction.endSlot; i < n; i++) {
|
||||
var attachment = skeletonDrawOrderItems[i].attachment;
|
||||
|
||||
if (attachment is RegionAttachment) {
|
||||
thisTriangles[triangleIndex] = afv; thisTriangles[triangleIndex + 1] = afv + 2; thisTriangles[triangleIndex + 2] = afv + 1;
|
||||
thisTriangles[triangleIndex + 3] = afv + 2; thisTriangles[triangleIndex + 4] = afv + 3; thisTriangles[triangleIndex + 5] = afv + 1;
|
||||
|
||||
triangleIndex += 6;
|
||||
afv += 4;
|
||||
} else {
|
||||
int[] attachmentTriangles;
|
||||
int attachmentVertexCount;
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2
|
||||
attachmentTriangles = meshAttachment.triangles;
|
||||
} else {
|
||||
var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (weightedMeshAttachment != null) {
|
||||
attachmentVertexCount = weightedMeshAttachment.uvs.Length >> 1; // length/2
|
||||
attachmentTriangles = weightedMeshAttachment.triangles;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
|
||||
thisTriangles[triangleIndex] = afv + attachmentTriangles[ii];
|
||||
|
||||
afv += attachmentVertexCount;
|
||||
}
|
||||
} // Done adding current submesh triangles
|
||||
}
|
||||
|
||||
Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin);
|
||||
Vector3 meshCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
|
||||
|
||||
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, attachmentList, instruction);
|
||||
mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
|
||||
|
||||
if (structureDoesntMatch) {
|
||||
mesh.triangles = triangles;
|
||||
}
|
||||
|
||||
return smartMesh.mesh;
|
||||
}
|
||||
|
||||
readonly DoubleBuffered<ArraysSingleSubmeshGenerator.SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
|
||||
readonly ExposedList<Attachment> attachmentListBuffer = new ExposedList<Attachment>();
|
||||
|
||||
float[] attachmentVertexBuffer = new float[8];
|
||||
Vector3[] meshVertices;
|
||||
Color32[] meshColors32;
|
||||
Vector2[] meshUVs;
|
||||
int[] triangles;
|
||||
|
||||
class SmartMesh {
|
||||
public readonly Mesh mesh = SpineMesh.NewMesh();
|
||||
SubmeshInstruction instructionsUsed;
|
||||
readonly ExposedList<Attachment> attachmentsUsed = new ExposedList<Attachment>();
|
||||
|
||||
public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, ExposedList<Attachment> attachmentList, SubmeshInstruction instructions) {
|
||||
mesh.vertices = verts;
|
||||
mesh.uv = uvs;
|
||||
mesh.colors32 = colors;
|
||||
instructionsUsed = instructions;
|
||||
|
||||
attachmentsUsed.Clear();
|
||||
attachmentsUsed.GrowIfNeeded(attachmentList.Capacity);
|
||||
attachmentsUsed.Count = attachmentList.Count;
|
||||
attachmentList.CopyTo(attachmentsUsed.Items);
|
||||
}
|
||||
|
||||
public bool StructureDoesntMatch (ExposedList<Attachment> attachmentList, SubmeshInstruction instructions) {
|
||||
// Check each submesh instructions for equal arrangement.
|
||||
var thisInstructions = instructionsUsed;
|
||||
if (
|
||||
instructions.skeleton != thisInstructions.skeleton ||
|
||||
instructions.material.GetInstanceID() != thisInstructions.material.GetInstanceID() ||
|
||||
instructions.startSlot != thisInstructions.startSlot ||
|
||||
instructions.endSlot != thisInstructions.endSlot ||
|
||||
instructions.triangleCount != thisInstructions.triangleCount ||
|
||||
instructions.vertexCount != thisInstructions.vertexCount
|
||||
) return true;
|
||||
//Debug.Log("structure matched");
|
||||
|
||||
// Check count inequality.
|
||||
if (attachmentList.Count != this.attachmentsUsed.Count) return true;
|
||||
var attachmentsPassed = attachmentList.Items;
|
||||
var myAttachments = this.attachmentsUsed.Items;
|
||||
for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
|
||||
if (attachmentsPassed[i] != myAttachments[i]) return true;
|
||||
|
||||
//Debug.Log("attachments matched");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bde87b725a537d4d9ff09a3ec7da8b5
|
||||
timeCreated: 1456493071
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,65 +1,74 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Spine;
|
||||
/******************************************************************************
|
||||
* 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 UnityEngine;
|
||||
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
public class ArraysSubmeshSetMeshGenerator : ISubmeshSetMeshGenerator {
|
||||
public class ArraysSubmeshSetMeshGenerator : ArraysMeshGenerator, ISubmeshSetMeshGenerator {
|
||||
#region Settings
|
||||
public float zSpacing = 0f;
|
||||
public bool premultiplyVertexColors = true;
|
||||
#endregion
|
||||
|
||||
readonly DoubleBuffered<SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
|
||||
readonly ExposedList<SubmeshInstruction> currentInstructions = new ExposedList<SubmeshInstruction>();
|
||||
readonly ExposedList<Attachment> currentAttachments = new ExposedList<Attachment>();
|
||||
readonly ExposedList<SubmeshTriangleBuffer> submeshBuffers = new ExposedList<SubmeshTriangleBuffer>();
|
||||
Material[] sharedMaterials = new Material[0];
|
||||
|
||||
public MeshAndMaterials GenerateMesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
|
||||
// STEP 0: Prepare instructions.
|
||||
var paramItems = instructions.Items;
|
||||
currentInstructions.Clear(false);
|
||||
for (int i = startSubmesh, n = endSubmesh; i < n; i++) {
|
||||
this.currentInstructions.Add(paramItems[i]);
|
||||
}
|
||||
|
||||
var smartMesh = doubleBufferedSmartMesh.GetNext();
|
||||
var mesh = smartMesh.mesh;
|
||||
|
||||
int submeshCount = currentInstructions.Count;
|
||||
var currentInstructionsItems = currentInstructions.Items;
|
||||
|
||||
int vertexCount = 0;
|
||||
for (int i = 0; i < submeshCount; i++) {
|
||||
// Ensure current instructions have correct cached values.
|
||||
currentInstructionsItems[i].firstVertexIndex = vertexCount;
|
||||
|
||||
// vertexCount will also be used for the rest of this method.
|
||||
vertexCount += currentInstructionsItems[i].vertexCount;
|
||||
}
|
||||
|
||||
float[] attVertBuffer = this.attachmentVertexBuffer;
|
||||
Vector2[] uvs = this.meshUVs;
|
||||
Color32[] colors32 = this.meshColors32;
|
||||
|
||||
// Ensure correct buffer sizes.
|
||||
Vector3[] vertices = this.meshVertices;
|
||||
|
||||
bool newVertices = vertices == null || vertexCount > vertices.Length;
|
||||
if (newVertices) {
|
||||
this.meshVertices = vertices = new Vector3[vertexCount];
|
||||
this.meshColors32 = colors32 = new Color32[vertexCount];
|
||||
this.meshUVs = uvs = new Vector2[vertexCount];
|
||||
} else {
|
||||
var zero = Vector3.zero;
|
||||
for (int i = vertexCount, n = this.meshVertices.Length; i < n; i++)
|
||||
vertices[i] = zero;
|
||||
currentInstructionsItems[i].firstVertexIndex = vertexCount;// Ensure current instructions have correct cached values.
|
||||
vertexCount += currentInstructionsItems[i].vertexCount; // vertexCount will also be used for the rest of this method.
|
||||
}
|
||||
|
||||
bool newSubmeshBuffers = submeshBuffers.Count < submeshCount;
|
||||
if (newSubmeshBuffers) {
|
||||
submeshBuffers.GrowIfNeeded(submeshCount);
|
||||
for (int i = submeshBuffers.Count; submeshBuffers.Count < submeshCount; i++) {
|
||||
submeshBuffers.Add(new SubmeshTriangleBuffer(currentInstructionsItems[i].triangleCount));
|
||||
}
|
||||
}
|
||||
// STEP 1: Ensure correct buffer sizes.
|
||||
bool vertBufferResized = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
|
||||
bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, currentInstructionsItems);
|
||||
|
||||
// STEP 2: Update buffers based on Skeleton.
|
||||
|
||||
// Initial values for manual Mesh Bounds calculation
|
||||
Vector3 meshBoundsMin;
|
||||
Vector3 meshBoundsMax;
|
||||
|
||||
float zSpacing = this.zSpacing;
|
||||
// Initial values for manual Mesh Bounds calculation
|
||||
if (vertexCount <= 0) {
|
||||
meshBoundsMin = new Vector3(0, 0, 0);
|
||||
meshBoundsMax = new Vector3(0, 0, 0);
|
||||
@ -69,250 +78,50 @@ namespace Spine.Unity.MeshGeneration {
|
||||
meshBoundsMax.x = int.MinValue;
|
||||
meshBoundsMax.y = int.MinValue;
|
||||
|
||||
int endSlot = currentInstructionsItems[submeshCount - 1].endSlot;
|
||||
if (zSpacing > 0f) {
|
||||
meshBoundsMin.z = 0f;
|
||||
meshBoundsMax.z = zSpacing * (currentInstructionsItems[submeshCount - 1].endSlot);
|
||||
meshBoundsMax.z = zSpacing * endSlot;
|
||||
} else {
|
||||
meshBoundsMin.z = zSpacing * (currentInstructionsItems[submeshCount - 1].endSlot);
|
||||
meshBoundsMin.z = zSpacing * endSlot;
|
||||
meshBoundsMax.z = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool structureDoesntMatch = newVertices || newSubmeshBuffers || smartMesh.StructureDoesntMatch(currentAttachments, currentInstructions);
|
||||
|
||||
// For each submesh, add vertex data from attachments.
|
||||
var currentAttachments_ = this.currentAttachments;
|
||||
bool structureDoesntMatch = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(currentAttachments_, currentInstructions);
|
||||
currentAttachments_.Clear(false);
|
||||
int vertexIndex = 0; // modified by FillVerts
|
||||
for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
|
||||
var currentInstruction = currentInstructionsItems[submeshIndex];
|
||||
int startSlot = currentInstruction.startSlot;
|
||||
int endSlot = currentInstruction.endSlot;
|
||||
var skeleton = currentInstruction.skeleton;
|
||||
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
|
||||
for (int i = startSlot; i < endSlot; i++) {
|
||||
var ca = skeletonDrawOrderItems[i].attachment;
|
||||
if (ca != null) currentAttachments_.Add(ca); // Includes BoundingBoxes. This is ok.
|
||||
}
|
||||
ArraysMeshGenerator.FillVerts(skeleton, startSlot, endSlot, zSpacing, this.premultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
|
||||
if (structureDoesntMatch) {
|
||||
var currentBuffer = submeshBuffers.Items[submeshIndex];
|
||||
bool isLastSubmesh = (submeshIndex == submeshCount - 1);
|
||||
ArraysMeshGenerator.FillTriangles(skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, startSlot, endSlot, ref currentBuffer.triangles, ref currentBuffer.triangleCount, isLastSubmesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (structureDoesntMatch) {
|
||||
mesh.Clear();
|
||||
|
||||
if (submeshCount == sharedMaterials.Length)
|
||||
currentInstructions.FillMaterialArray(this.sharedMaterials);
|
||||
else
|
||||
this.sharedMaterials = currentInstructions.GetNewMaterialArray();
|
||||
this.sharedMaterials = currentInstructions.GetUpdatedMaterialArray(this.sharedMaterials);
|
||||
}
|
||||
|
||||
currentAttachments.Clear(false);
|
||||
int vertexIndex = 0;
|
||||
// For each submesh, add vertex data from attachments.
|
||||
for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
|
||||
var currentSubmeshInstruction = currentInstructionsItems[submeshIndex];
|
||||
var skeleton = currentSubmeshInstruction.skeleton;
|
||||
|
||||
// for (int slotIndex = currentSubmeshInstruction.startSlot, endSlot = currentSubmeshInstruction.endSlot; slotIndex < endSlot; slotIndex++) {
|
||||
// var slot = skeletonDrawOrderItems[slotIndex];
|
||||
// var attachment = slot.attachment;
|
||||
// float z = slotIndex * zSpacing;
|
||||
//
|
||||
// var regionAttachment = attachment as RegionAttachment;
|
||||
// if (regionAttachment != null) {
|
||||
// regionAttachment.ComputeWorldVertices(slot.bone, attVertBuffer);
|
||||
//
|
||||
// float x1 = attVertBuffer[RegionAttachment.X1], y1 = attVertBuffer[RegionAttachment.Y1];
|
||||
// float x2 = attVertBuffer[RegionAttachment.X2], y2 = attVertBuffer[RegionAttachment.Y2];
|
||||
// float x3 = attVertBuffer[RegionAttachment.X3], y3 = attVertBuffer[RegionAttachment.Y3];
|
||||
// float x4 = attVertBuffer[RegionAttachment.X4], y4 = attVertBuffer[RegionAttachment.Y4];
|
||||
// vertices[vertexIndex].x = x1; vertices[vertexIndex].y = y1; vertices[vertexIndex].z = z;
|
||||
// vertices[vertexIndex + 1].x = x4; vertices[vertexIndex + 1].y = y4; vertices[vertexIndex + 1].z = z;
|
||||
// vertices[vertexIndex + 2].x = x2; vertices[vertexIndex + 2].y = y2; vertices[vertexIndex + 2].z = z;
|
||||
// vertices[vertexIndex + 3].x = x3; vertices[vertexIndex + 3].y = y3; vertices[vertexIndex + 3].z = z;
|
||||
//
|
||||
// if (premultiplyVertexColors) {
|
||||
// color.a = (byte)(a * slot.a * regionAttachment.a);
|
||||
// color.r = (byte)(r * slot.r * regionAttachment.r * color.a);
|
||||
// color.g = (byte)(g * slot.g * regionAttachment.g * color.a);
|
||||
// color.b = (byte)(b * slot.b * regionAttachment.b * color.a);
|
||||
// if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
// } else {
|
||||
// color.a = (byte)(a * slot.a * regionAttachment.a);
|
||||
// color.r = (byte)(r * slot.r * regionAttachment.r * 255);
|
||||
// color.g = (byte)(g * slot.g * regionAttachment.g * 255);
|
||||
// color.b = (byte)(b * slot.b * regionAttachment.b * 255);
|
||||
// }
|
||||
//
|
||||
// colors32[vertexIndex] = color; colors32[vertexIndex + 1] = color; colors32[vertexIndex + 2] = color; colors32[vertexIndex + 3] = color;
|
||||
//
|
||||
// float[] regionUVs = regionAttachment.uvs;
|
||||
// uvs[vertexIndex].x = regionUVs[RegionAttachment.X1]; uvs[vertexIndex].y = regionUVs[RegionAttachment.Y1];
|
||||
// uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4]; uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4];
|
||||
// uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2]; uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2];
|
||||
// uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3]; uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3];
|
||||
//
|
||||
// // Calculate min/max X
|
||||
// if (x1 < meshBoundsMin.x) meshBoundsMin.x = x1;
|
||||
// else if (x1 > meshBoundsMax.x) meshBoundsMax.x = x1;
|
||||
// if (x2 < meshBoundsMin.x) meshBoundsMin.x = x2;
|
||||
// else if (x2 > meshBoundsMax.x) meshBoundsMax.x = x2;
|
||||
// if (x3 < meshBoundsMin.x) meshBoundsMin.x = x3;
|
||||
// else if (x3 > meshBoundsMax.x) meshBoundsMax.x = x3;
|
||||
// if (x4 < meshBoundsMin.x) meshBoundsMin.x = x4;
|
||||
// else if (x4 > meshBoundsMax.x) meshBoundsMax.x = x4;
|
||||
//
|
||||
// // Calculate min/max Y
|
||||
// if (y1 < meshBoundsMin.y) meshBoundsMin.y = y1;
|
||||
// else if (y1 > meshBoundsMax.y) meshBoundsMax.y = y1;
|
||||
// if (y2 < meshBoundsMin.y) meshBoundsMin.y = y2;
|
||||
// else if (y2 > meshBoundsMax.y) meshBoundsMax.y = y2;
|
||||
// if (y3 < meshBoundsMin.y) meshBoundsMin.y = y3;
|
||||
// else if (y3 > meshBoundsMax.y) meshBoundsMax.y = y3;
|
||||
// if (y4 < meshBoundsMin.y) meshBoundsMin.y = y4;
|
||||
// else if (y4 > meshBoundsMax.y) meshBoundsMax.y = y4;
|
||||
//
|
||||
// currentAttachments.Add(regionAttachment);
|
||||
// vertexIndex += 4;
|
||||
// } else {
|
||||
// var meshAttachment = attachment as MeshAttachment;
|
||||
// if (meshAttachment != null) {
|
||||
// int meshVertexCount = meshAttachment.vertices.Length;
|
||||
// if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
|
||||
// meshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
||||
//
|
||||
// if (premultiplyVertexColors) {
|
||||
// color.a = (byte)(a * slot.a * meshAttachment.a);
|
||||
// color.r = (byte)(r * slot.r * meshAttachment.r * color.a);
|
||||
// color.g = (byte)(g * slot.g * meshAttachment.g * color.a);
|
||||
// color.b = (byte)(b * slot.b * meshAttachment.b * color.a);
|
||||
// if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
// } else {
|
||||
// color.a = (byte)(a * slot.a * meshAttachment.a);
|
||||
// color.r = (byte)(r * slot.r * meshAttachment.r * 255);
|
||||
// color.g = (byte)(g * slot.g * meshAttachment.g * 255);
|
||||
// color.b = (byte)(b * slot.b * meshAttachment.b * 255);
|
||||
// }
|
||||
//
|
||||
// float[] attachmentUVs = meshAttachment.uvs;
|
||||
// for (int iii = 0; iii < meshVertexCount; iii += 2) {
|
||||
// float x = attVertBuffer[iii], y = attVertBuffer[iii + 1];
|
||||
// vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z;
|
||||
// colors32[vertexIndex] = color; uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 1];
|
||||
//
|
||||
// if (x < meshBoundsMin.x) meshBoundsMin.x = x;
|
||||
// else if (x > meshBoundsMax.x) meshBoundsMax.x = x;
|
||||
//
|
||||
// if (y < meshBoundsMin.y) meshBoundsMin.y = y;
|
||||
// else if (y > meshBoundsMax.y) meshBoundsMax.y = y;
|
||||
//
|
||||
// currentAttachments.Add(meshAttachment);
|
||||
// vertexIndex++;
|
||||
// }
|
||||
// } else {
|
||||
// var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
// if (weightedMeshAttachment != null) {
|
||||
// int meshVertexCount = weightedMeshAttachment.uvs.Length;
|
||||
// if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
|
||||
// weightedMeshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
||||
//
|
||||
// if (premultiplyVertexColors) {
|
||||
// color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
|
||||
// color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a);
|
||||
// color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a);
|
||||
// color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a);
|
||||
// if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
||||
// } else {
|
||||
// color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
|
||||
// color.r = (byte)(r * slot.r * weightedMeshAttachment.r * 255);
|
||||
// color.g = (byte)(g * slot.g * weightedMeshAttachment.g * 255);
|
||||
// color.b = (byte)(b * slot.b * weightedMeshAttachment.b * 255);
|
||||
// }
|
||||
//
|
||||
// float[] attachmentUVs = weightedMeshAttachment.uvs;
|
||||
// for (int iii = 0; iii < meshVertexCount; iii += 2) {
|
||||
// float x = attVertBuffer[iii], y = attVertBuffer[iii + 1];
|
||||
// vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z;
|
||||
// colors32[vertexIndex] = color;
|
||||
// uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 1];
|
||||
//
|
||||
// if (x < meshBoundsMin.x) meshBoundsMin.x = x;
|
||||
// else if (x > meshBoundsMax.x) meshBoundsMax.x = x;
|
||||
// if (y < meshBoundsMin.y) meshBoundsMin.y = y;
|
||||
// else if (y > meshBoundsMax.y) meshBoundsMax.y = y;
|
||||
//
|
||||
// currentAttachments.Add(weightedMeshAttachment);
|
||||
// vertexIndex++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
|
||||
int startSlot = currentSubmeshInstruction.startSlot;
|
||||
int endSlot = currentSubmeshInstruction.endSlot;
|
||||
|
||||
for (int i = startSlot, n = currentSubmeshInstruction.endSlot; i < n; i++) {
|
||||
var ca = skeletonDrawOrderItems[i].attachment;
|
||||
if (ca != null) {
|
||||
// Includes BoundingBoxes. This is ok.
|
||||
currentAttachments.Add(ca);
|
||||
}
|
||||
}
|
||||
|
||||
ArraysBuffers.Fill(skeleton, startSlot, endSlot, zSpacing, this.premultiplyVertexColors, vertices, uvs, colors32, ref vertexIndex, ref attVertBuffer, ref meshBoundsMin, ref meshBoundsMax);
|
||||
|
||||
// Push triangles in this submesh
|
||||
if (structureDoesntMatch) {
|
||||
smartMesh.mesh.Clear(); // rebuild triangle array.
|
||||
|
||||
var currentSubmesh = submeshBuffers.Items[submeshIndex];
|
||||
bool isLastSubmesh = (submeshIndex == submeshCount - 1);
|
||||
|
||||
int triangleCount = currentSubmesh.triangleCount = currentSubmeshInstruction.triangleCount;
|
||||
int trianglesCapacity = currentSubmesh.triangles.Length;
|
||||
|
||||
int[] triangles = currentSubmesh.triangles;
|
||||
if (isLastSubmesh) {
|
||||
if (trianglesCapacity > triangleCount) {
|
||||
for (int i = triangleCount; i < trianglesCapacity; i++)
|
||||
triangles[i] = 0;
|
||||
}
|
||||
} else if (trianglesCapacity != triangleCount) {
|
||||
triangles = currentSubmesh.triangles = new int[triangleCount];
|
||||
currentSubmesh.triangleCount = 0;
|
||||
}
|
||||
|
||||
// Iterate through submesh slots and store the triangles.
|
||||
int triangleIndex = 0;
|
||||
int afv = currentSubmeshInstruction.firstVertexIndex; // attachment first vertex
|
||||
|
||||
for (int i = currentSubmeshInstruction.startSlot, n = currentSubmeshInstruction.endSlot; i < n; i++) {
|
||||
var attachment = skeletonDrawOrderItems[i].attachment;
|
||||
|
||||
if (attachment is RegionAttachment) {
|
||||
triangles[triangleIndex] = afv; triangles[triangleIndex + 1] = afv + 2; triangles[triangleIndex + 2] = afv + 1;
|
||||
triangles[triangleIndex + 3] = afv + 2; triangles[triangleIndex + 4] = afv + 3; triangles[triangleIndex + 5] = afv + 1;
|
||||
|
||||
triangleIndex += 6;
|
||||
afv += 4;
|
||||
} else {
|
||||
int[] attachmentTriangles;
|
||||
int attachmentVertexCount;
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2
|
||||
attachmentTriangles = meshAttachment.triangles;
|
||||
} else {
|
||||
var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (weightedMeshAttachment != null) {
|
||||
attachmentVertexCount = weightedMeshAttachment.uvs.Length >> 1; // length/2
|
||||
attachmentTriangles = weightedMeshAttachment.triangles;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
|
||||
triangles[triangleIndex] = afv + attachmentTriangles[ii];
|
||||
|
||||
afv += attachmentVertexCount;
|
||||
}
|
||||
} // Done adding current submesh triangles
|
||||
}
|
||||
}
|
||||
|
||||
this.attachmentVertexBuffer = attVertBuffer;
|
||||
Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin);
|
||||
Vector3 meshCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
|
||||
|
||||
// STEP 3: Assign the buffers into the Mesh.
|
||||
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, currentAttachments, currentInstructions);
|
||||
mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
|
||||
mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
|
||||
|
||||
if (structureDoesntMatch) {
|
||||
// push new triangles if doesn't match.
|
||||
// Push new triangles if doesn't match.
|
||||
mesh.subMeshCount = submeshCount;
|
||||
for (int i = 0; i < submeshCount; i++)
|
||||
mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
|
||||
@ -321,19 +130,6 @@ namespace Spine.Unity.MeshGeneration {
|
||||
return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);
|
||||
}
|
||||
|
||||
#region Internals
|
||||
readonly DoubleBuffered<SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
|
||||
readonly ExposedList<SubmeshInstruction> currentInstructions = new ExposedList<SubmeshInstruction>();
|
||||
readonly ExposedList<Attachment> currentAttachments = new ExposedList<Attachment>();
|
||||
|
||||
float[] attachmentVertexBuffer = new float[8];
|
||||
Vector3[] meshVertices;
|
||||
Color32[] meshColors32;
|
||||
Vector2[] meshUVs;
|
||||
Material[] sharedMaterials = new Material[0];
|
||||
readonly ExposedList<SubmeshTriangleBuffer> submeshBuffers = new ExposedList<SubmeshTriangleBuffer>();
|
||||
#endregion
|
||||
|
||||
#region Types
|
||||
// A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it.
|
||||
class SmartMesh {
|
||||
|
||||
@ -1,21 +1,57 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
//#define SPINE_OPTIONAL_NORMALS
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
public class ArraysSubmeshedMeshGenerator : ISubmeshedMeshGenerator {
|
||||
/// <summary>
|
||||
/// Arrays submeshed mesh generator.
|
||||
/// </summary>
|
||||
public class ArraysSubmeshedMeshGenerator : ArraysMeshGenerator, ISubmeshedMeshGenerator {
|
||||
|
||||
readonly List<Slot> separators = new List<Slot>();
|
||||
public List<Slot> Separators { get { return this.separators; } }
|
||||
|
||||
public bool premultiplyVertexColors = true;
|
||||
public float zSpacing = 0f;
|
||||
#if SPINE_OPTIONAL_NORMALS
|
||||
public bool generateNormals;
|
||||
public bool generateTangents;
|
||||
#endif
|
||||
|
||||
// public bool generateNormals;
|
||||
// public bool generateTangents;
|
||||
readonly DoubleBuffered<SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
|
||||
readonly SubmeshedMeshInstruction currentInstructions = new SubmeshedMeshInstruction();
|
||||
readonly ExposedList<SubmeshTriangleBuffer> submeshBuffers = new ExposedList<SubmeshTriangleBuffer>();
|
||||
Material[] sharedMaterials = new Material[0];
|
||||
|
||||
public SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton) {
|
||||
if (skeleton == null) throw new System.ArgumentNullException("skeleton");
|
||||
@ -71,8 +107,8 @@ namespace Spine.Unity.MeshGeneration {
|
||||
var attachmentMaterial = (Material)((AtlasRegion)rendererObject).page.rendererObject;
|
||||
|
||||
// Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
|
||||
if (( runningVertexCount > 0 && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) ||
|
||||
( separatorCount > 0 && separators.Contains(slot) )) {
|
||||
bool separatedBySlot = ( separatorCount > 0 && separators.Contains(slot) );
|
||||
if (( runningVertexCount > 0 && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) || separatedBySlot) {
|
||||
|
||||
instructionList.Add(
|
||||
new SubmeshInstruction {
|
||||
@ -82,7 +118,8 @@ namespace Spine.Unity.MeshGeneration {
|
||||
vertexCount = submeshVertexCount,
|
||||
startSlot = submeshStartSlotIndex,
|
||||
endSlot = i,
|
||||
firstVertexIndex = submeshFirstVertex
|
||||
firstVertexIndex = submeshFirstVertex,
|
||||
separatedBySlot = separatedBySlot
|
||||
}
|
||||
);
|
||||
|
||||
@ -109,7 +146,8 @@ namespace Spine.Unity.MeshGeneration {
|
||||
vertexCount = submeshVertexCount,
|
||||
startSlot = submeshStartSlotIndex,
|
||||
endSlot = drawOrderCount,
|
||||
firstVertexIndex = submeshFirstVertex
|
||||
firstVertexIndex = submeshFirstVertex,
|
||||
separatedBySlot = false
|
||||
}
|
||||
);
|
||||
|
||||
@ -117,50 +155,25 @@ namespace Spine.Unity.MeshGeneration {
|
||||
return currentInstructions;
|
||||
}
|
||||
|
||||
// ISubmeshedMeshGenerator.GenerateMesh
|
||||
/// <summary>Generates a mesh based on SubmeshedMeshInstructions</summary>
|
||||
public MeshAndMaterials GenerateMesh (SubmeshedMeshInstruction meshInstructions) {
|
||||
var smartMesh = doubleBufferedSmartMesh.GetNext();
|
||||
var mesh = smartMesh.mesh;
|
||||
|
||||
int submeshCount = meshInstructions.submeshInstructions.Count;
|
||||
|
||||
var instructionList = meshInstructions.submeshInstructions;
|
||||
float zSpacing = this.zSpacing;
|
||||
float[] attVertBuffer = this.attachmentVertexBuffer;
|
||||
Vector2[] uvs = this.meshUVs;
|
||||
Color32[] colors32 = this.meshColors32;
|
||||
|
||||
// Ensure correct buffer sizes.
|
||||
// STEP 1: Ensure correct buffer sizes.
|
||||
bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, instructionList.Items);
|
||||
bool vertBufferResized = ArraysMeshGenerator.EnsureSize(meshInstructions.vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
|
||||
Vector3[] vertices = this.meshVertices;
|
||||
|
||||
bool newVertices = vertices == null || meshInstructions.vertexCount > vertices.Length;
|
||||
int instructionVertexCount = meshInstructions.vertexCount;
|
||||
if (newVertices) {
|
||||
this.meshVertices = vertices = new Vector3[instructionVertexCount];
|
||||
this.meshColors32 = colors32 = new Color32[instructionVertexCount];
|
||||
this.meshUVs = uvs = new Vector2[instructionVertexCount];
|
||||
} else {
|
||||
var zero = Vector3.zero;
|
||||
for (int i = instructionVertexCount, n = this.meshVertices.Length; i < n; i++)
|
||||
vertices[i] = zero;
|
||||
}
|
||||
|
||||
bool newSubmeshBuffers = submeshBuffers.Count < submeshCount;
|
||||
if (newSubmeshBuffers) {
|
||||
submeshBuffers.GrowIfNeeded(submeshCount);
|
||||
for (int i = submeshBuffers.Count; submeshBuffers.Count < submeshCount; i++) {
|
||||
submeshBuffers.Add(new SubmeshTriangleBuffer(instructionList.Items[i].triangleCount));
|
||||
//submeshBuffers.Items[i] = new SubmeshTriangleBuffer(tc);
|
||||
//submeshBuffers.Count = i;
|
||||
}
|
||||
}
|
||||
|
||||
// STEP 2: Update buffers based on Skeleton.
|
||||
float zSpacing = this.zSpacing;
|
||||
Vector3 meshBoundsMin;
|
||||
Vector3 meshBoundsMax;
|
||||
|
||||
int attachmentCount = meshInstructions.attachmentList.Count;
|
||||
|
||||
// Initial values for manual Mesh Bounds calculation
|
||||
if (meshInstructions.attachmentList.Count <= 0) {
|
||||
if (attachmentCount <= 0) {
|
||||
meshBoundsMin = new Vector3(0, 0, 0);
|
||||
meshBoundsMax = new Vector3(0, 0, 0);
|
||||
} else {
|
||||
@ -177,134 +190,60 @@ namespace Spine.Unity.MeshGeneration {
|
||||
meshBoundsMax.z = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool structureDoesntMatch = newVertices || newSubmeshBuffers || smartMesh.StructureDoesntMatch(meshInstructions);
|
||||
|
||||
if (structureDoesntMatch) {
|
||||
mesh.Clear();
|
||||
|
||||
if (submeshCount == sharedMaterials.Length)
|
||||
meshInstructions.FillMaterialArray(this.sharedMaterials);
|
||||
else
|
||||
this.sharedMaterials = meshInstructions.GetNewMaterialArray();
|
||||
}
|
||||
|
||||
int vertexIndex = 0;
|
||||
|
||||
// For each submesh, add vertex data from attachments.
|
||||
bool structureDoesntMatch = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(meshInstructions);
|
||||
// For each submesh, add vertex data from attachments. Also triangles, but only if needed.
|
||||
int vertexIndex = 0; // modified by FillVerts
|
||||
for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
|
||||
var currentSubmeshInstruction = instructionList.Items[submeshIndex];
|
||||
var skeleton = currentSubmeshInstruction.skeleton;
|
||||
|
||||
ArraysBuffers.Fill(skeleton, currentSubmeshInstruction.startSlot, currentSubmeshInstruction.endSlot, zSpacing, this.premultiplyVertexColors, vertices, uvs, colors32, ref vertexIndex, ref attVertBuffer, ref meshBoundsMin, ref meshBoundsMax);
|
||||
|
||||
// Push triangles in this submesh
|
||||
var submeshInstruction = instructionList.Items[submeshIndex];
|
||||
int start = submeshInstruction.startSlot;
|
||||
int end = submeshInstruction.endSlot;
|
||||
var skeleton = submeshInstruction.skeleton;
|
||||
ArraysMeshGenerator.FillVerts(skeleton, start, end, zSpacing, this.premultiplyVertexColors, vertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
|
||||
if (structureDoesntMatch) {
|
||||
smartMesh.mesh.Clear(); // rebuild triangle array.
|
||||
|
||||
var currentSubmesh = submeshBuffers.Items[submeshIndex];
|
||||
var currentBuffer = submeshBuffers.Items[submeshIndex];
|
||||
bool isLastSubmesh = (submeshIndex == submeshCount - 1);
|
||||
|
||||
int triangleCount = currentSubmesh.triangleCount = currentSubmeshInstruction.triangleCount;
|
||||
int trianglesCapacity = currentSubmesh.triangles.Length;
|
||||
|
||||
int[] triangles = currentSubmesh.triangles;
|
||||
if (isLastSubmesh) {
|
||||
if (trianglesCapacity > triangleCount) {
|
||||
for (int i = triangleCount; i < trianglesCapacity; i++)
|
||||
triangles[i] = 0;
|
||||
}
|
||||
} else if (trianglesCapacity != triangleCount) {
|
||||
triangles = currentSubmesh.triangles = new int[triangleCount];
|
||||
currentSubmesh.triangleCount = 0;
|
||||
}
|
||||
|
||||
// Iterate through submesh slots and store the triangles.
|
||||
int triangleIndex = 0;
|
||||
int afv = currentSubmeshInstruction.firstVertexIndex; // attachment first vertex
|
||||
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
|
||||
for (int i = currentSubmeshInstruction.startSlot, n = currentSubmeshInstruction.endSlot; i < n; i++) {
|
||||
var attachment = skeletonDrawOrderItems[i].attachment;
|
||||
|
||||
if (attachment is RegionAttachment) {
|
||||
triangles[triangleIndex] = afv; triangles[triangleIndex + 1] = afv + 2; triangles[triangleIndex + 2] = afv + 1;
|
||||
triangles[triangleIndex + 3] = afv + 2; triangles[triangleIndex + 4] = afv + 3; triangles[triangleIndex + 5] = afv + 1;
|
||||
|
||||
triangleIndex += 6;
|
||||
afv += 4;
|
||||
} else {
|
||||
int[] attachmentTriangles;
|
||||
int attachmentVertexCount;
|
||||
var meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2
|
||||
attachmentTriangles = meshAttachment.triangles;
|
||||
} else {
|
||||
var weightedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||
if (weightedMeshAttachment != null) {
|
||||
attachmentVertexCount = weightedMeshAttachment.uvs.Length >> 1; // length/2
|
||||
attachmentTriangles = weightedMeshAttachment.triangles;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
|
||||
triangles[triangleIndex] = afv + attachmentTriangles[ii];
|
||||
|
||||
afv += attachmentVertexCount;
|
||||
}
|
||||
} // Done adding current submesh triangles
|
||||
ArraysMeshGenerator.FillTriangles(skeleton, submeshInstruction.triangleCount, submeshInstruction.firstVertexIndex, start, end, ref currentBuffer.triangles, ref currentBuffer.triangleCount, isLastSubmesh);
|
||||
}
|
||||
}
|
||||
|
||||
this.attachmentVertexBuffer = attVertBuffer;
|
||||
Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin);
|
||||
Vector3 meshCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
|
||||
if (structureDoesntMatch) {
|
||||
mesh.Clear();
|
||||
this.sharedMaterials = meshInstructions.GetUpdatedMaterialArray(this.sharedMaterials);
|
||||
}
|
||||
|
||||
// STEP 3: Assign the buffers into the Mesh.
|
||||
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, meshInstructions);
|
||||
mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
|
||||
mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
|
||||
|
||||
if (structureDoesntMatch) {
|
||||
|
||||
// if (generateNormals) {
|
||||
// int vertexCount = meshInstructions.vertexCount;
|
||||
// Vector3[] normals = new Vector3[vertexCount];
|
||||
// Vector3 normal = new Vector3(0, 0, -1);
|
||||
// for (int i = 0; i < vertexCount; i++)
|
||||
// normals[i] = normal;
|
||||
// mesh.normals = normals;
|
||||
//
|
||||
// if (generateTangents) {
|
||||
// Vector4[] tangents = new Vector4[vertexCount];
|
||||
// Vector4 tangent = new Vector4(1, 0, 0, -1);
|
||||
// for (int i = 0; i < vertexCount; i++)
|
||||
// tangents[i] = tangent;
|
||||
// mesh.tangents = tangents;
|
||||
// }
|
||||
// }
|
||||
|
||||
// push new triangles if doesn't match.
|
||||
// Push new triangles if doesn't match.
|
||||
mesh.subMeshCount = submeshCount;
|
||||
for (int i = 0; i < submeshCount; i++)
|
||||
mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
|
||||
|
||||
#if SPINE_OPTIONAL_NORMALS
|
||||
if (generateNormals) {
|
||||
int vertexCount = meshInstructions.vertexCount;
|
||||
Vector3[] normals = new Vector3[vertexCount];
|
||||
Vector3 normal = new Vector3(0, 0, -1);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
normals[i] = normal;
|
||||
mesh.normals = normals;
|
||||
|
||||
if (generateTangents) {
|
||||
Vector4[] tangents = new Vector4[vertexCount];
|
||||
Vector4 tangent = new Vector4(1, 0, 0, -1);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
tangents[i] = tangent;
|
||||
mesh.tangents = tangents;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);
|
||||
}
|
||||
|
||||
#region Internals
|
||||
readonly DoubleBuffered<SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
|
||||
readonly SubmeshedMeshInstruction currentInstructions = new SubmeshedMeshInstruction();
|
||||
|
||||
float[] attachmentVertexBuffer = new float[8];
|
||||
Vector3[] meshVertices;
|
||||
Color32[] meshColors32;
|
||||
Vector2[] meshUVs;
|
||||
Material[] sharedMaterials = new Material[0];
|
||||
readonly ExposedList<SubmeshTriangleBuffer> submeshBuffers = new ExposedList<SubmeshTriangleBuffer>();
|
||||
#endregion
|
||||
|
||||
#region Types
|
||||
// A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it.
|
||||
class SmartMesh {
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
public class SubmeshTriangleBuffer {
|
||||
public int[] triangles;
|
||||
public int triangleCount;
|
||||
|
||||
public SubmeshTriangleBuffer (int triangleCount) {
|
||||
triangles = new int[triangleCount];
|
||||
this.triangleCount = triangleCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Spine.Unity {
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
// Typically, each ISpineMeshGenerator implementation will handle double-buffering meshes, handling any other optimization behavior
|
||||
// and operating on assumptions (eg, only handling one skeleton, not updating triangles all the time).
|
||||
// The Scale property allows generated mesh to match external systems like Canvas referencePixelsPerUnit
|
||||
@ -24,32 +24,15 @@ namespace Spine.Unity.MeshGeneration {
|
||||
MeshAndMaterials GenerateMesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh);
|
||||
}
|
||||
|
||||
// ISingleSubmeshGenerator
|
||||
// How to use:
|
||||
// Step 1: Have a single SubmeshInstruction
|
||||
// Step 2:
|
||||
public interface ISingleSubmeshGenerator {
|
||||
Mesh GenerateMesh (SubmeshInstruction instruction);
|
||||
}
|
||||
|
||||
/// <summary>Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes.</summary>
|
||||
public class SubmeshedMeshInstruction {
|
||||
public readonly ExposedList<SubmeshInstruction> submeshInstructions = new ExposedList<SubmeshInstruction>();
|
||||
public readonly ExposedList<Attachment> attachmentList = new ExposedList<Attachment>();
|
||||
public int vertexCount = -1;
|
||||
|
||||
/// <summary>Allocates a new material array to render this mesh and its constituent submeshes.</summary>
|
||||
public Material[] GetNewMaterialArray () {
|
||||
var materials = new Material[submeshInstructions.Count];
|
||||
FillMaterialArray(materials);
|
||||
return materials;
|
||||
}
|
||||
|
||||
/// <summary>Fills a given array with the materials needed to render this submeshed mesh.</summary>
|
||||
public void FillMaterialArray (Material[] materialArray) {
|
||||
var instructionsItems = submeshInstructions.Items;
|
||||
for (int i = 0, n = materialArray.Length; i < n; i++)
|
||||
materialArray[i] = instructionsItems[i].material;
|
||||
/// <summary>Returns a material array of the SubmeshedMeshInstruction. Fills the passed array if it's the correct size. Creates a new array if it's a different size.</summary>
|
||||
public Material[] GetUpdatedMaterialArray (Material[] materials) {
|
||||
return submeshInstructions.GetUpdatedMaterialArray(materials);
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,14 +57,16 @@ namespace Spine.Unity.MeshGeneration {
|
||||
}
|
||||
|
||||
public static class SubmeshInstructionExtensions {
|
||||
public static void FillMaterialArray (this ExposedList<SubmeshInstruction> instructions, Material[] materialArray) {
|
||||
for (int i = 0, n = materialArray.Length; i < n; i++)
|
||||
materialArray[i] = instructions.Items[i].material;
|
||||
}
|
||||
/// <summary>Returns a material array of the instructions. Fills the passed array if it's the correct size. Creates a new array if it's a different size.</summary>
|
||||
public static Material[] GetUpdatedMaterialArray (this ExposedList<SubmeshInstruction> instructions, Material[] materials) {
|
||||
int submeshCount = instructions.Count;
|
||||
|
||||
if (submeshCount != materials.Length)
|
||||
materials = new Material[submeshCount];
|
||||
|
||||
for (int i = 0, n = materials.Length; i < n; i++)
|
||||
materials[i] = instructions.Items[i].material;
|
||||
|
||||
public static Material[] GetNewMaterialArray (this ExposedList<SubmeshInstruction> instructions) {
|
||||
var materials = new Material[instructions.Count];
|
||||
instructions.FillMaterialArray(materials);
|
||||
return materials;
|
||||
}
|
||||
}
|
||||
@ -94,6 +79,5 @@ namespace Spine.Unity.MeshGeneration {
|
||||
this.mesh = mesh;
|
||||
this.materials = materials;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e1503fd3294b7042a676e58798e4136
|
||||
folderAsset: yes
|
||||
timeCreated: 1455486312
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93a799664eb27fd4183aed06719b306c
|
||||
folderAsset: yes
|
||||
timeCreated: 1455486322
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -28,18 +28,16 @@
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#if (UNITY_5_0 || UNITY_5_1 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
|
||||
#define PREUNITY_5_2
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
|
||||
#if !(PREUNITY_5_2)
|
||||
namespace Spine.Unity {
|
||||
/// <summary>This is for testing and educational purposes only. This takes about 10 times longer to render than ArraySpineMeshGenerator.</summary>
|
||||
namespace Spine.Unity.MeshGeneration {
|
||||
/// <summary>This is for testing and educational purposes only. This takes about 10 times longer to render than the Arrays implementations.</summary>
|
||||
public class VertexHelperSpineMeshGenerator : ISimpleMeshGenerator {
|
||||
|
||||
public float scale = 1f;
|
||||
|
||||
@ -1,195 +1,226 @@
|
||||
#if (UNITY_5_0 || UNITY_5_1 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
#if (UNITY_5_0 || UNITY_5_1 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
|
||||
#define PREUNITY_5_2
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
using UnityEditor;
|
||||
using Spine;
|
||||
|
||||
[CustomEditor(typeof(SkeletonGraphic))]
|
||||
public class SkeletonGraphicInspector : Editor {
|
||||
SerializedProperty material_, color_;
|
||||
SerializedProperty skeletonDataAsset_, initialSkinName_;
|
||||
SerializedProperty startingAnimation_, startingLoop_, timeScale_, freeze_;
|
||||
#if !PREUNITY_5_2
|
||||
SerializedProperty raycastTarget_;
|
||||
namespace Spine.Unity {
|
||||
|
||||
[CustomEditor(typeof(SkeletonGraphic))]
|
||||
public class SkeletonGraphicInspector : Editor {
|
||||
SerializedProperty material_, color_;
|
||||
SerializedProperty skeletonDataAsset_, initialSkinName_;
|
||||
SerializedProperty startingAnimation_, startingLoop_, timeScale_, freeze_;
|
||||
#if !PREUNITY_5_2
|
||||
SerializedProperty raycastTarget_;
|
||||
|
||||
SkeletonGraphic thisSkeletonGraphic;
|
||||
SkeletonGraphic thisSkeletonGraphic;
|
||||
|
||||
void OnEnable () {
|
||||
var so = this.serializedObject;
|
||||
thisSkeletonGraphic = target as SkeletonGraphic;
|
||||
void OnEnable () {
|
||||
var so = this.serializedObject;
|
||||
thisSkeletonGraphic = target as SkeletonGraphic;
|
||||
|
||||
// MaskableGraphic
|
||||
material_ = so.FindProperty("m_Material");
|
||||
color_ = so.FindProperty("m_Color");
|
||||
raycastTarget_ = so.FindProperty("m_RaycastTarget");
|
||||
// MaskableGraphic
|
||||
material_ = so.FindProperty("m_Material");
|
||||
color_ = so.FindProperty("m_Color");
|
||||
raycastTarget_ = so.FindProperty("m_RaycastTarget");
|
||||
|
||||
// SkeletonRenderer
|
||||
skeletonDataAsset_ = so.FindProperty("skeletonDataAsset");
|
||||
initialSkinName_ = so.FindProperty("initialSkinName");
|
||||
// SkeletonRenderer
|
||||
skeletonDataAsset_ = so.FindProperty("skeletonDataAsset");
|
||||
initialSkinName_ = so.FindProperty("initialSkinName");
|
||||
|
||||
// SkeletonAnimation
|
||||
startingAnimation_ = so.FindProperty("startingAnimation");
|
||||
startingLoop_ = so.FindProperty("startingLoop");
|
||||
timeScale_ = so.FindProperty("timeScale");
|
||||
freeze_ = so.FindProperty("freeze");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI () {
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(skeletonDataAsset_);
|
||||
EditorGUILayout.PropertyField(material_);
|
||||
EditorGUILayout.PropertyField(color_);
|
||||
|
||||
if (thisSkeletonGraphic.skeletonDataAsset == null) {
|
||||
EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
serializedObject.Update();
|
||||
return;
|
||||
// SkeletonAnimation
|
||||
startingAnimation_ = so.FindProperty("startingAnimation");
|
||||
startingLoop_ = so.FindProperty("startingLoop");
|
||||
timeScale_ = so.FindProperty("timeScale");
|
||||
freeze_ = so.FindProperty("freeze");
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(initialSkinName_);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(startingAnimation_);
|
||||
EditorGUILayout.PropertyField(startingLoop_);
|
||||
EditorGUILayout.PropertyField(timeScale_);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(freeze_);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("UI", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(raycastTarget_);
|
||||
public override void OnInspectorGUI () {
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
bool wasChanged = EditorGUI.EndChangeCheck();
|
||||
EditorGUILayout.PropertyField(skeletonDataAsset_);
|
||||
EditorGUILayout.PropertyField(material_);
|
||||
EditorGUILayout.PropertyField(color_);
|
||||
|
||||
if (wasChanged) {
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
#region Menus
|
||||
[MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
|
||||
static void MatchRectTransformWithBounds (MenuCommand command) {
|
||||
var skeletonGraphic = (SkeletonGraphic)command.context;
|
||||
var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh;
|
||||
|
||||
var bounds = mesh.bounds;
|
||||
var size = bounds.size;
|
||||
var center = bounds.center;
|
||||
var p = new Vector2(
|
||||
0.5f - (center.x / size.x),
|
||||
0.5f - (center.y / size.y)
|
||||
);
|
||||
|
||||
skeletonGraphic.rectTransform.sizeDelta = size;
|
||||
skeletonGraphic.rectTransform.pivot = p;
|
||||
}
|
||||
|
||||
public static Material DefaultSkeletonGraphicMaterial {
|
||||
get {
|
||||
var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material");
|
||||
if (guids.Length <= 0)
|
||||
return null;
|
||||
var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
if (string.IsNullOrEmpty(firstAssetPath))
|
||||
return null;
|
||||
var firstMaterial = AssetDatabase.LoadAssetAtPath<Material>(firstAssetPath);
|
||||
return firstMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Spine/SkeletonGraphic (UnityUI)", false, 10)]
|
||||
static public void SkeletonGraphicCreateMenuItem () {
|
||||
var parentGameObject = Selection.activeObject as GameObject;
|
||||
var parentTransform = parentGameObject == null ? null : parentGameObject.GetComponent<RectTransform>();
|
||||
|
||||
if (parentTransform == null) {
|
||||
Debug.LogWarning("Your new SkeletonGraphic will not be visible until it is placed under a Canvas");
|
||||
}
|
||||
|
||||
var gameObject = NewSkeletonGraphicGameObject("New SkeletonGraphic");
|
||||
gameObject.transform.SetParent(parentTransform, false);
|
||||
EditorUtility.FocusProjectWindow();
|
||||
Selection.activeObject = gameObject;
|
||||
EditorGUIUtility.PingObject(Selection.activeObject);
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Spine/Instantiate (UnityUI)", false, 0)]
|
||||
static void InstantiateSkeletonGraphic () {
|
||||
Object[] arr = Selection.objects;
|
||||
foreach (Object o in arr) {
|
||||
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
|
||||
string skinName = EditorPrefs.GetString(guid + "_lastSkin", "");
|
||||
|
||||
InstantiateSkeletonGraphic((SkeletonDataAsset)o, skinName);
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Spine/Instantiate (UnityUI)", true, 0)]
|
||||
static bool ValidateInstantiateSkeletonGraphic () {
|
||||
Object[] arr = Selection.objects;
|
||||
|
||||
if (arr.Length == 0)
|
||||
return false;
|
||||
|
||||
foreach (var selected in arr) {
|
||||
if (selected.GetType() != typeof(SkeletonDataAsset))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, string skinName) {
|
||||
return InstantiateSkeletonGraphic(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
|
||||
}
|
||||
|
||||
public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
|
||||
string spineGameObjectName = string.Format("SkeletonGraphic ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
|
||||
var go = NewSkeletonGraphicGameObject(spineGameObjectName);
|
||||
var graphic = go.GetComponent<SkeletonGraphic>();
|
||||
graphic.skeletonDataAsset = skeletonDataAsset;
|
||||
|
||||
SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
|
||||
|
||||
if (data == null) {
|
||||
for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
|
||||
string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
|
||||
skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
|
||||
if (thisSkeletonGraphic.skeletonDataAsset == null) {
|
||||
EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
serializedObject.Update();
|
||||
return;
|
||||
}
|
||||
|
||||
data = skeletonDataAsset.GetSkeletonData(true);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(initialSkinName_);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(startingAnimation_);
|
||||
EditorGUILayout.PropertyField(startingLoop_);
|
||||
EditorGUILayout.PropertyField(timeScale_);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(freeze_);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("UI", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(raycastTarget_);
|
||||
|
||||
bool wasChanged = EditorGUI.EndChangeCheck();
|
||||
|
||||
if (wasChanged) {
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
if (skin == null)
|
||||
skin = data.DefaultSkin;
|
||||
#region Menus
|
||||
[MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
|
||||
static void MatchRectTransformWithBounds (MenuCommand command) {
|
||||
var skeletonGraphic = (SkeletonGraphic)command.context;
|
||||
var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh;
|
||||
|
||||
if (skin == null)
|
||||
skin = data.Skins.Items[0];
|
||||
var bounds = mesh.bounds;
|
||||
var size = bounds.size;
|
||||
var center = bounds.center;
|
||||
var p = new Vector2(
|
||||
0.5f - (center.x / size.x),
|
||||
0.5f - (center.y / size.y)
|
||||
);
|
||||
|
||||
graphic.Initialize(false);
|
||||
graphic.Skeleton.SetSkin(skin);
|
||||
graphic.initialSkinName = skin.Name;
|
||||
graphic.Skeleton.UpdateWorldTransform();
|
||||
graphic.UpdateMesh();
|
||||
skeletonGraphic.rectTransform.sizeDelta = size;
|
||||
skeletonGraphic.rectTransform.pivot = p;
|
||||
}
|
||||
|
||||
return graphic;
|
||||
public static Material DefaultSkeletonGraphicMaterial {
|
||||
get {
|
||||
var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material");
|
||||
if (guids.Length <= 0)
|
||||
return null;
|
||||
var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
if (string.IsNullOrEmpty(firstAssetPath))
|
||||
return null;
|
||||
var firstMaterial = AssetDatabase.LoadAssetAtPath<Material>(firstAssetPath);
|
||||
return firstMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Spine/SkeletonGraphic (UnityUI)", false, 10)]
|
||||
static public void SkeletonGraphicCreateMenuItem () {
|
||||
var parentGameObject = Selection.activeObject as GameObject;
|
||||
var parentTransform = parentGameObject == null ? null : parentGameObject.GetComponent<RectTransform>();
|
||||
|
||||
if (parentTransform == null) {
|
||||
Debug.LogWarning("Your new SkeletonGraphic will not be visible until it is placed under a Canvas");
|
||||
}
|
||||
|
||||
var gameObject = NewSkeletonGraphicGameObject("New SkeletonGraphic");
|
||||
gameObject.transform.SetParent(parentTransform, false);
|
||||
EditorUtility.FocusProjectWindow();
|
||||
Selection.activeObject = gameObject;
|
||||
EditorGUIUtility.PingObject(Selection.activeObject);
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Spine/Instantiate (UnityUI)", false, 0)]
|
||||
static void InstantiateSkeletonGraphic () {
|
||||
Object[] arr = Selection.objects;
|
||||
foreach (Object o in arr) {
|
||||
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
|
||||
string skinName = EditorPrefs.GetString(guid + "_lastSkin", "");
|
||||
|
||||
InstantiateSkeletonGraphic((SkeletonDataAsset)o, skinName);
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Spine/Instantiate (UnityUI)", true, 0)]
|
||||
static bool ValidateInstantiateSkeletonGraphic () {
|
||||
Object[] arr = Selection.objects;
|
||||
|
||||
if (arr.Length == 0)
|
||||
return false;
|
||||
|
||||
foreach (var selected in arr) {
|
||||
if (selected.GetType() != typeof(SkeletonDataAsset))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, string skinName) {
|
||||
return InstantiateSkeletonGraphic(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
|
||||
}
|
||||
|
||||
public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
|
||||
string spineGameObjectName = string.Format("SkeletonGraphic ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
|
||||
var go = NewSkeletonGraphicGameObject(spineGameObjectName);
|
||||
var graphic = go.GetComponent<SkeletonGraphic>();
|
||||
graphic.skeletonDataAsset = skeletonDataAsset;
|
||||
|
||||
SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
|
||||
|
||||
if (data == null) {
|
||||
for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
|
||||
string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
|
||||
skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
|
||||
}
|
||||
|
||||
data = skeletonDataAsset.GetSkeletonData(true);
|
||||
}
|
||||
|
||||
if (skin == null)
|
||||
skin = data.DefaultSkin;
|
||||
|
||||
if (skin == null)
|
||||
skin = data.Skins.Items[0];
|
||||
|
||||
graphic.Initialize(false);
|
||||
graphic.Skeleton.SetSkin(skin);
|
||||
graphic.initialSkinName = skin.Name;
|
||||
graphic.Skeleton.UpdateWorldTransform();
|
||||
graphic.UpdateMesh();
|
||||
|
||||
return graphic;
|
||||
}
|
||||
|
||||
static GameObject NewSkeletonGraphicGameObject (string gameObjectName) {
|
||||
var go = new GameObject(gameObjectName, typeof(RectTransform), typeof(CanvasRenderer), typeof(SkeletonGraphic));
|
||||
var graphic = go.GetComponent<SkeletonGraphic>();
|
||||
graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial;
|
||||
return go;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static GameObject NewSkeletonGraphicGameObject (string gameObjectName) {
|
||||
var go = new GameObject(gameObjectName, typeof(RectTransform), typeof(CanvasRenderer), typeof(SkeletonGraphic));
|
||||
var graphic = go.GetComponent<SkeletonGraphic>();
|
||||
graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial;
|
||||
return go;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -28,194 +28,194 @@
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#if (UNITY_5_0 || UNITY_5_1 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7)
|
||||
#define PREUNITY_5_2
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEngine.UI;
|
||||
using Spine;
|
||||
|
||||
[ExecuteInEditMode, RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent]
|
||||
[AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")]
|
||||
public class SkeletonGraphic : MaskableGraphic {
|
||||
namespace Spine.Unity {
|
||||
[ExecuteInEditMode, RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent]
|
||||
[AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")]
|
||||
public class SkeletonGraphic : MaskableGraphic {
|
||||
|
||||
#region Inspector
|
||||
public SkeletonDataAsset skeletonDataAsset;
|
||||
#region Inspector
|
||||
public SkeletonDataAsset skeletonDataAsset;
|
||||
|
||||
[SpineSkin(dataField:"skeletonDataAsset")]
|
||||
public string initialSkinName = "default";
|
||||
[SpineSkin(dataField:"skeletonDataAsset")]
|
||||
public string initialSkinName = "default";
|
||||
|
||||
[SpineAnimation(dataField:"skeletonDataAsset")]
|
||||
public string startingAnimation;
|
||||
public bool startingLoop;
|
||||
public float timeScale = 1f;
|
||||
public bool freeze;
|
||||
[SpineAnimation(dataField:"skeletonDataAsset")]
|
||||
public string startingAnimation;
|
||||
public bool startingLoop;
|
||||
public float timeScale = 1f;
|
||||
public bool freeze;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate () {
|
||||
// This handles Scene View preview.
|
||||
base.OnValidate ();
|
||||
#if !PREUNITY_5_2
|
||||
if (this.IsValid) {
|
||||
if (skeletonDataAsset == null) {
|
||||
Clear();
|
||||
startingAnimation = "";
|
||||
} else if (skeletonDataAsset.GetSkeletonData(true) != skeleton.data) {
|
||||
Clear();
|
||||
Initialize(true);
|
||||
startingAnimation = "";
|
||||
if (skeletonDataAsset.atlasAssets.Length > 1 || skeletonDataAsset.atlasAssets[0].materials.Length > 1) {
|
||||
Debug.LogError("Unity UI does not support multiple textures per Renderer. Your skeleton will not be rendered correctly. Recommend using SkeletonAnimation instead. This requires the use of a Screen space camera canvas.");
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate () {
|
||||
// This handles Scene View preview.
|
||||
base.OnValidate ();
|
||||
#if !PREUNITY_5_2
|
||||
if (this.IsValid) {
|
||||
if (skeletonDataAsset == null) {
|
||||
Clear();
|
||||
startingAnimation = "";
|
||||
} else if (skeletonDataAsset.GetSkeletonData(true) != skeleton.data) {
|
||||
Clear();
|
||||
Initialize(true);
|
||||
startingAnimation = "";
|
||||
if (skeletonDataAsset.atlasAssets.Length > 1 || skeletonDataAsset.atlasAssets[0].materials.Length > 1) {
|
||||
Debug.LogError("Unity UI does not support multiple textures per Renderer. Your skeleton will not be rendered correctly. Recommend using SkeletonAnimation instead. This requires the use of a Screen space camera canvas.");
|
||||
}
|
||||
} else {
|
||||
if (freeze) return;
|
||||
skeleton.SetToSetupPose();
|
||||
if (!string.IsNullOrEmpty(startingAnimation)) {
|
||||
skeleton.PoseWithAnimation(startingAnimation, 0f, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (freeze) return;
|
||||
skeleton.SetToSetupPose();
|
||||
if (!string.IsNullOrEmpty(startingAnimation)) {
|
||||
skeleton.PoseWithAnimation(startingAnimation, 0f, false);
|
||||
}
|
||||
if (skeletonDataAsset != null)
|
||||
Initialize(true);
|
||||
}
|
||||
#else
|
||||
Debug.LogWarning("SkeletonGraphic requres Unity 5.2 or higher.\nUnityEngine.UI 5.1 and below does not accept meshes and can't be used to render Spine skeletons. You may delete the SkeletonGraphic folder under `Modules` if you want to exclude it from your project." );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
protected override void Reset () {
|
||||
base.Reset();
|
||||
if (canvas == null) {
|
||||
Debug.LogWarningFormat("SkeletonGraphic requires a Canvas to be visible. Move this GameObject ({0}) in the Hierarchy so it becomes a child of a Canvas.", gameObject.name);
|
||||
}
|
||||
|
||||
if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)")) {
|
||||
Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material.");
|
||||
}
|
||||
} else {
|
||||
if (skeletonDataAsset != null)
|
||||
Initialize(true);
|
||||
}
|
||||
#else
|
||||
Debug.LogWarning("SkeletonGraphic requres Unity 5.2 or higher.\nUnityEngine.UI 5.1 and below does not accept meshes and can't be used to render Spine skeletons. You may delete the SkeletonGraphic folder under `Modules` if you want to exclude it from your project." );
|
||||
#endif
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected override void Reset () {
|
||||
base.Reset();
|
||||
if (canvas == null) {
|
||||
Debug.LogWarningFormat("SkeletonGraphic requires a Canvas to be visible. Move this GameObject ({0}) in the Hierarchy so it becomes a child of a Canvas.", gameObject.name);
|
||||
#if !PREUNITY_5_2
|
||||
#region Internals
|
||||
// This is used by the UI system to determine what to put in the MaterialPropertyBlock.
|
||||
public override Texture mainTexture {
|
||||
get {
|
||||
// Fail loudly when incorrectly set up.
|
||||
return skeletonDataAsset == null ? null : skeletonDataAsset.atlasAssets[0].materials[0].mainTexture;
|
||||
}
|
||||
}
|
||||
|
||||
if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)")) {
|
||||
Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material.");
|
||||
protected override void Awake () {
|
||||
base.Awake ();
|
||||
if (!this.IsValid) {
|
||||
Initialize(false);
|
||||
Rebuild(CanvasUpdate.PreRender);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#if !PREUNITY_5_2
|
||||
#region Internals
|
||||
// This is used by the UI system to determine what to put in the MaterialPropertyBlock.
|
||||
public override Texture mainTexture {
|
||||
get {
|
||||
// Fail loudly when incorrectly set up.
|
||||
return skeletonDataAsset == null ? null : skeletonDataAsset.atlasAssets[0].materials[0].mainTexture;
|
||||
public override void Rebuild (CanvasUpdate update) {
|
||||
base.Rebuild(update);
|
||||
if (canvasRenderer.cull) return;
|
||||
if (update == CanvasUpdate.PreRender) UpdateMesh();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Awake () {
|
||||
base.Awake ();
|
||||
if (!this.IsValid) {
|
||||
Initialize(false);
|
||||
Rebuild(CanvasUpdate.PreRender);
|
||||
public virtual void Update () {
|
||||
if (freeze) return;
|
||||
Update(Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Rebuild (CanvasUpdate update) {
|
||||
base.Rebuild(update);
|
||||
if (canvasRenderer.cull) return;
|
||||
if (update == CanvasUpdate.PreRender) UpdateMesh();
|
||||
}
|
||||
public virtual void Update (float deltaTime) {
|
||||
if (!this.IsValid) return;
|
||||
|
||||
public virtual void Update () {
|
||||
if (freeze) return;
|
||||
Update(Time.deltaTime);
|
||||
}
|
||||
deltaTime *= timeScale;
|
||||
skeleton.Update(deltaTime);
|
||||
state.Update(deltaTime);
|
||||
state.Apply(skeleton);
|
||||
|
||||
public virtual void Update (float deltaTime) {
|
||||
if (!this.IsValid) return;
|
||||
if (UpdateLocal != null) UpdateLocal(this);
|
||||
|
||||
deltaTime *= timeScale;
|
||||
skeleton.Update(deltaTime);
|
||||
state.Update(deltaTime);
|
||||
state.Apply(skeleton);
|
||||
|
||||
if (UpdateLocal != null) UpdateLocal(this);
|
||||
|
||||
skeleton.UpdateWorldTransform();
|
||||
|
||||
if (UpdateWorld != null) {
|
||||
UpdateWorld(this);
|
||||
skeleton.UpdateWorldTransform();
|
||||
|
||||
if (UpdateWorld != null) {
|
||||
UpdateWorld(this);
|
||||
skeleton.UpdateWorldTransform();
|
||||
}
|
||||
|
||||
if (UpdateComplete != null) UpdateComplete(this);
|
||||
}
|
||||
|
||||
if (UpdateComplete != null) UpdateComplete(this);
|
||||
}
|
||||
void LateUpdate () {
|
||||
if (freeze) return;
|
||||
//this.SetVerticesDirty(); // Which is better?
|
||||
UpdateMesh();
|
||||
}
|
||||
#endregion
|
||||
|
||||
void LateUpdate () {
|
||||
if (freeze) return;
|
||||
//this.SetVerticesDirty(); // Which is better?
|
||||
UpdateMesh();
|
||||
}
|
||||
#endregion
|
||||
#region API
|
||||
protected Skeleton skeleton;
|
||||
public Skeleton Skeleton { get { return skeleton; } }
|
||||
public SkeletonData SkeletonData { get { return skeleton == null ? null : skeleton.data; } }
|
||||
public bool IsValid { get { return skeleton != null; } }
|
||||
|
||||
#region API
|
||||
protected Skeleton skeleton;
|
||||
public Skeleton Skeleton { get { return skeleton; } }
|
||||
public SkeletonData SkeletonData { get { return skeleton == null ? null : skeleton.data; } }
|
||||
public bool IsValid { get { return skeleton != null; } }
|
||||
protected Spine.AnimationState state;
|
||||
public Spine.AnimationState AnimationState { get { return state; } }
|
||||
|
||||
protected Spine.AnimationState state;
|
||||
public Spine.AnimationState AnimationState { get { return state; } }
|
||||
// This is any object that can give you a mesh when you give it a skeleton to render.
|
||||
protected Spine.Unity.MeshGeneration.ISimpleMeshGenerator spineMeshGenerator;
|
||||
public Spine.Unity.MeshGeneration.ISimpleMeshGenerator SpineMeshGenerator { get { return this.spineMeshGenerator; } }
|
||||
|
||||
// This is any object that can give you a mesh when you give it a skeleton to render.
|
||||
protected Spine.Unity.ISimpleMeshGenerator spineMeshGenerator;
|
||||
public Spine.Unity.ISimpleMeshGenerator SpineMeshGenerator { get { return this.spineMeshGenerator; } }
|
||||
public delegate void UpdateDelegate (SkeletonGraphic skeletonGraphic);
|
||||
public event UpdateDelegate UpdateLocal;
|
||||
public event UpdateDelegate UpdateWorld;
|
||||
public event UpdateDelegate UpdateComplete;
|
||||
|
||||
public delegate void UpdateDelegate (SkeletonGraphic skeletonGraphic);
|
||||
public event UpdateDelegate UpdateLocal;
|
||||
public event UpdateDelegate UpdateWorld;
|
||||
public event UpdateDelegate UpdateComplete;
|
||||
|
||||
public void Clear () {
|
||||
skeleton = null;
|
||||
canvasRenderer.Clear();
|
||||
}
|
||||
|
||||
public void Initialize (bool overwrite) {
|
||||
if (this.IsValid && !overwrite) return;
|
||||
|
||||
// Make sure none of the stuff is null
|
||||
if (this.skeletonDataAsset == null) return;
|
||||
var skeletonData = this.skeletonDataAsset.GetSkeletonData(false);
|
||||
if (skeletonData == null) return;
|
||||
|
||||
if (skeletonDataAsset.atlasAssets.Length <= 0 || skeletonDataAsset.atlasAssets[0].materials.Length <= 0) return;
|
||||
|
||||
this.state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
|
||||
if (state == null) {
|
||||
Clear();
|
||||
return;
|
||||
public void Clear () {
|
||||
skeleton = null;
|
||||
canvasRenderer.Clear();
|
||||
}
|
||||
|
||||
this.skeleton = new Skeleton(skeletonData);
|
||||
this.spineMeshGenerator = new Spine.Unity.ArraysSimpleMeshGenerator(); // You can switch this out with any other implementer of ISpineMeshGenerator
|
||||
public void Initialize (bool overwrite) {
|
||||
if (this.IsValid && !overwrite) return;
|
||||
|
||||
// Set the initial Skin and Animation
|
||||
if (!string.IsNullOrEmpty(initialSkinName))
|
||||
skeleton.SetSkin(initialSkinName);
|
||||
// Make sure none of the stuff is null
|
||||
if (this.skeletonDataAsset == null) return;
|
||||
var skeletonData = this.skeletonDataAsset.GetSkeletonData(false);
|
||||
if (skeletonData == null) return;
|
||||
|
||||
if (!string.IsNullOrEmpty(startingAnimation))
|
||||
state.SetAnimation(0, startingAnimation, startingLoop);
|
||||
}
|
||||
if (skeletonDataAsset.atlasAssets.Length <= 0 || skeletonDataAsset.atlasAssets[0].materials.Length <= 0) return;
|
||||
|
||||
public void UpdateMesh () {
|
||||
if (this.IsValid) {
|
||||
skeleton.SetColor(this.color);
|
||||
if (canvas != null)
|
||||
spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; // TODO: move this to a listener to of the canvas?
|
||||
this.state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
|
||||
if (state == null) {
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
canvasRenderer.SetMesh(spineMeshGenerator.GenerateMesh(skeleton));
|
||||
this.UpdateMaterial();
|
||||
this.skeleton = new Skeleton(skeletonData);
|
||||
this.spineMeshGenerator = new Spine.Unity.MeshGeneration.ArraysSimpleMeshGenerator(); // You can switch this out with any other implementer of Spine.Unity.MeshGeneration.ISimpleMeshGenerator
|
||||
|
||||
// Set the initial Skin and Animation
|
||||
if (!string.IsNullOrEmpty(initialSkinName))
|
||||
skeleton.SetSkin(initialSkinName);
|
||||
|
||||
if (!string.IsNullOrEmpty(startingAnimation))
|
||||
state.SetAnimation(0, startingAnimation, startingLoop);
|
||||
}
|
||||
|
||||
public void UpdateMesh () {
|
||||
if (this.IsValid) {
|
||||
skeleton.SetColor(this.color);
|
||||
if (canvas != null)
|
||||
spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; // TODO: move this to a listener to of the canvas?
|
||||
|
||||
canvasRenderer.SetMesh(spineMeshGenerator.GenerateMesh(skeleton));
|
||||
this.UpdateMaterial();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,9 @@
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
#define SPINE_OPTIONAL_NORMALS
|
||||
#define SPINE_OPTIONAL_FRONTFACING
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@ -46,10 +49,14 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
public String initialSkinName;
|
||||
|
||||
#region Advanced
|
||||
#if SPINE_OPTIONAL_NORMALS
|
||||
public bool calculateNormals, calculateTangents;
|
||||
#endif
|
||||
public float zSpacing;
|
||||
public bool renderMeshes = true, immutableTriangles;
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
public bool frontFacing;
|
||||
#endif
|
||||
public bool logErrors = false;
|
||||
|
||||
// Submesh Separation
|
||||
@ -83,8 +90,10 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
|
||||
Material[] sharedMaterials = new Material[0];
|
||||
|
||||
#if SPINE_OPTIONAL_NORMALS
|
||||
Vector3[] normals;
|
||||
Vector4[] tangents;
|
||||
#endif
|
||||
|
||||
#region Runtime Instantiation
|
||||
public static T NewSpineGameObject<T> (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
|
||||
@ -169,7 +178,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
if (!meshRenderer.enabled && GenerateMeshOverride == null)
|
||||
return;
|
||||
|
||||
// Step 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes.
|
||||
// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes.
|
||||
|
||||
// This method caches several .Items arrays.
|
||||
// Never mutate their overlying ExposedList objects.
|
||||
@ -187,12 +196,13 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
workingAttachments.Count = drawOrderCount;
|
||||
var workingAttachmentsItems = workingInstruction.attachments.Items;
|
||||
|
||||
// SPINE_DETECT_FLIPS
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
var workingFlips = workingInstruction.attachmentFlips;
|
||||
workingFlips.Clear(false);
|
||||
workingFlips.GrowIfNeeded(drawOrderCount);
|
||||
workingFlips.Count = drawOrderCount;
|
||||
var workingFlipsItems = workingFlips.Items;
|
||||
#endif
|
||||
|
||||
var workingSubmeshInstructions = workingInstruction.submeshInstructions; // Items array should not be cached. There is dynamic writing to this list.
|
||||
workingSubmeshInstructions.Clear(false);
|
||||
@ -209,9 +219,10 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
|
||||
workingAttachmentsItems[i] = attachment;
|
||||
|
||||
// SPINE_DETECT_FLIPS
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
bool flip = frontFacing && (slot.bone.WorldSignX != slot.bone.WorldSignY);
|
||||
workingFlipsItems[i] = flip;
|
||||
#endif
|
||||
|
||||
object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
|
||||
int attachmentVertexCount, attachmentTriangleCount;
|
||||
@ -298,14 +309,16 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
|
||||
workingInstruction.vertexCount = vertexCount;
|
||||
workingInstruction.immutableTriangles = this.immutableTriangles;
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
workingInstruction.frontFacing = this.frontFacing;
|
||||
#endif
|
||||
|
||||
if (GenerateMeshOverride != null) {
|
||||
GenerateMeshOverride(workingInstruction);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2. Update vertex buffer based on verts from the attachments.
|
||||
// STEP 2. Update vertex buffer based on verts from the attachments.
|
||||
// Uses values that were also stored in workingInstruction.
|
||||
Vector3[] vertices = this.vertices;
|
||||
bool vertexCountIncreased = vertexCount > vertices.Length;
|
||||
@ -315,6 +328,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
this.colors = new Color32[vertexCount];
|
||||
this.uvs = new Vector2[vertexCount];
|
||||
|
||||
#if SPINE_OPTIONAL_NORMALS
|
||||
if (calculateNormals) {
|
||||
Vector3[] localNormals = this.normals = new Vector3[vertexCount];
|
||||
Vector3 normal = new Vector3(0, 0, -1);
|
||||
@ -328,6 +342,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
localTangents[i] = tangent;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
Vector3 zero = Vector3.zero;
|
||||
for (int i = vertexCount, n = vertices.Length; i < n; i++)
|
||||
@ -526,6 +541,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
currentMesh.colors32 = colors;
|
||||
currentMesh.uv = uvs;
|
||||
var currentSmartMeshInstructionUsed = currentSmartMesh.instructionUsed;
|
||||
#if SPINE_OPTIONAL_NORMALS
|
||||
if (currentSmartMeshInstructionUsed.vertexCount < vertexCount) {
|
||||
if (calculateNormals) {
|
||||
currentMesh.normals = normals;
|
||||
@ -534,6 +550,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if the triangles should also be updated.
|
||||
// This thorough structure check is cheaper than updating triangles every frame.
|
||||
@ -553,7 +570,11 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
for (int i = 0, last = submeshCount - 1; i < submeshCount; i++) {
|
||||
var submeshInstruction = workingSubmeshInstructions.Items[i];
|
||||
if (mutableTriangles || i >= oldSubmeshCount)
|
||||
SetSubmesh(i, submeshInstruction, currentInstructions.attachmentFlips, i == last);
|
||||
SetSubmesh(i, submeshInstruction,
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
currentInstructions.attachmentFlips,
|
||||
#endif
|
||||
i == last);
|
||||
thisSubmeshMaterials.Add(submeshInstruction.material);
|
||||
}
|
||||
|
||||
@ -627,7 +648,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
return true;
|
||||
}
|
||||
|
||||
// SPINE_DETECT_FLIPS
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
if (a.frontFacing != b.frontFacing) { // if settings changed
|
||||
return true;
|
||||
} else if (a.frontFacing) { // if settings matched, only need to check one.
|
||||
@ -638,6 +659,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Submesh count changed
|
||||
int submeshCountA = a.submeshInstructions.Count;
|
||||
@ -664,8 +686,12 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, ExposedList<bool> flipStates, bool isLastSubmesh) {
|
||||
#else
|
||||
void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, bool isLastSubmesh) {
|
||||
#endif
|
||||
SubmeshTriangleBuffer currentSubmesh = submeshes.Items[submeshIndex];
|
||||
int[] triangles = currentSubmesh.triangles;
|
||||
|
||||
@ -686,7 +712,11 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
currentSubmesh.triangleCount = 0;
|
||||
}
|
||||
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
if (!this.renderMeshes && !this.frontFacing) {
|
||||
#else
|
||||
if (!this.renderMeshes) {
|
||||
#endif
|
||||
// Use stored triangles if possible.
|
||||
if (currentSubmesh.firstVertex != firstVertex || currentSubmesh.triangleCount < triangleCount) { //|| currentSubmesh.triangleCount == 0
|
||||
currentSubmesh.triangleCount = triangleCount;
|
||||
@ -707,20 +737,20 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
// This method caches several .Items arrays.
|
||||
// Never mutate their overlying ExposedList objects.
|
||||
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
var flipStatesItems = flipStates.Items;
|
||||
#endif
|
||||
|
||||
// Iterate through all slots and store their triangles.
|
||||
var drawOrderItems = skeleton.DrawOrder.Items;
|
||||
// SPINE_DETECT_FLIPS
|
||||
var flipStatesItems = flipStates.Items;
|
||||
|
||||
int triangleIndex = 0; // Modified by loop
|
||||
for (int i = submeshInstructions.startSlot, n = submeshInstructions.endSlot; i < n; i++) {
|
||||
Attachment attachment = drawOrderItems[i].attachment;
|
||||
// SPINE_DETECT_FLIPS
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
bool flip = frontFacing && flipStatesItems[i];
|
||||
|
||||
// Add RegionAttachment triangles
|
||||
if (attachment is RegionAttachment) {
|
||||
// SPINE_DETECT_FLIPS
|
||||
if (!flip) {
|
||||
triangles[triangleIndex] = firstVertex;
|
||||
triangles[triangleIndex + 1] = firstVertex + 2;
|
||||
@ -741,6 +771,20 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
firstVertex += 4;
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if (attachment is RegionAttachment) {
|
||||
triangles[triangleIndex] = firstVertex;
|
||||
triangles[triangleIndex + 1] = firstVertex + 2;
|
||||
triangles[triangleIndex + 2] = firstVertex + 1;
|
||||
triangles[triangleIndex + 3] = firstVertex + 2;
|
||||
triangles[triangleIndex + 4] = firstVertex + 3;
|
||||
triangles[triangleIndex + 5] = firstVertex + 1;
|
||||
|
||||
triangleIndex += 6;
|
||||
firstVertex += 4;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add (Weighted)MeshAttachment triangles
|
||||
int[] attachmentTriangles;
|
||||
@ -758,7 +802,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
continue;
|
||||
}
|
||||
|
||||
// SPINE_DETECT_FLIPS
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
if (flip) {
|
||||
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3) {
|
||||
triangles[triangleIndex + 2] = firstVertex + attachmentTriangles[ii];
|
||||
@ -770,6 +814,12 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
triangles[triangleIndex] = firstVertex + attachmentTriangles[ii];
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) {
|
||||
triangles[triangleIndex] = firstVertex + attachmentTriangles[ii];
|
||||
}
|
||||
#endif
|
||||
|
||||
firstVertex += attachmentVertexCount;
|
||||
}
|
||||
}
|
||||
@ -797,21 +847,26 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
|
||||
public class Instruction {
|
||||
public bool immutableTriangles;
|
||||
public bool frontFacing;
|
||||
public int vertexCount = -1;
|
||||
public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
|
||||
public readonly ExposedList<bool> attachmentFlips = new ExposedList<bool>();
|
||||
public readonly ExposedList<Spine.Unity.MeshGeneration.SubmeshInstruction> submeshInstructions = new ExposedList<Spine.Unity.MeshGeneration.SubmeshInstruction>();
|
||||
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
public bool frontFacing;
|
||||
public readonly ExposedList<bool> attachmentFlips = new ExposedList<bool>();
|
||||
#endif
|
||||
|
||||
public void Clear () {
|
||||
this.attachments.Clear(false);
|
||||
this.attachmentFlips.Clear(false);
|
||||
this.submeshInstructions.Clear(false);
|
||||
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
this.attachmentFlips.Clear(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Set (Instruction other) {
|
||||
this.immutableTriangles = other.immutableTriangles;
|
||||
this.frontFacing = other.frontFacing;
|
||||
this.vertexCount = other.vertexCount;
|
||||
|
||||
this.attachments.Clear(false);
|
||||
@ -819,13 +874,14 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
this.attachments.Count = other.attachments.Count;
|
||||
other.attachments.CopyTo(this.attachments.Items);
|
||||
|
||||
// SPINE_DETECT_FLIPS
|
||||
#if SPINE_OPTIONAL_FRONTFACING
|
||||
this.frontFacing = other.frontFacing;
|
||||
this.attachmentFlips.Clear(false);
|
||||
this.attachmentFlips.GrowIfNeeded(other.attachmentFlips.Capacity);
|
||||
this.attachmentFlips.Count = other.attachmentFlips.Count;
|
||||
if (this.frontFacing)
|
||||
other.attachmentFlips.CopyTo(this.attachmentFlips.Items);
|
||||
|
||||
#endif
|
||||
|
||||
this.submeshInstructions.Clear(false);
|
||||
this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user