[unity] SkeletonRenderer Update (Tangents, MeshGeneration) (#646)

This commit is contained in:
John 2016-07-21 22:23:21 +08:00 committed by GitHub
parent b564ad4503
commit 77c4cdc1e4
11 changed files with 374 additions and 409 deletions

View File

@ -30,7 +30,6 @@
*****************************************************************************/
#define NO_PREFAB_MESH
using System;
using UnityEditor;
using UnityEngine;
@ -65,23 +64,24 @@ namespace Spine.Unity.Editor {
}
protected virtual void DrawInspectorGUI () {
// JOHN: todo: support multiediting.
SkeletonRenderer component = (SkeletonRenderer)target;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(skeletonDataAsset);
const string ReloadButtonLabel = "Reload";
float reloadWidth = GUI.skin.label.CalcSize(new GUIContent(ReloadButtonLabel)).x + 20;
if (GUILayout.Button(ReloadButtonLabel, GUILayout.Width(reloadWidth))) {
if (component.skeletonDataAsset != null) {
foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) {
if (aa != null)
aa.Reset();
using (new EditorGUILayout.HorizontalScope()) {
EditorGUILayout.PropertyField(skeletonDataAsset);
const string ReloadButtonLabel = "Reload";
float reloadWidth = GUI.skin.label.CalcSize(new GUIContent(ReloadButtonLabel)).x + 20;
if (GUILayout.Button(ReloadButtonLabel, GUILayout.Width(reloadWidth))) {
if (component.skeletonDataAsset != null) {
foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) {
if (aa != null)
aa.Reset();
}
component.skeletonDataAsset.Reset();
}
component.skeletonDataAsset.Reset();
component.Initialize(true);
}
component.Initialize(true);
}
EditorGUILayout.EndHorizontal();
if (!component.valid) {
component.Initialize(true);
@ -100,10 +100,10 @@ namespace Spine.Unity.Editor {
// Initial skin name.
{
String[] skins = new String[component.skeleton.Data.Skins.Count];
string[] skins = new string[component.skeleton.Data.Skins.Count];
int skinIndex = 0;
for (int i = 0; i < skins.Length; i++) {
String skinNameString = component.skeleton.Data.Skins.Items[i].Name;
string skinNameString = component.skeleton.Data.Skins.Items[i].Name;
skins[i] = skinNameString;
if (skinNameString == initialSkinName.stringValue)
skinIndex = i;
@ -115,40 +115,33 @@ namespace Spine.Unity.Editor {
EditorGUILayout.Space();
// Sorting Layers
{
SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true);
}
SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true);
// More Render Options...
{
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
EditorGUI.indentLevel++;
advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
if (advancedFoldout) {
EditorGUI.indentLevel++;
advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
if (advancedFoldout) {
EditorGUI.indentLevel++;
SeparatorsField(separatorSlotNames);
EditorGUILayout.PropertyField(meshes,
new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"));
EditorGUILayout.PropertyField(immutableTriangles,
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
EditorGUILayout.Space();
SeparatorsField(separatorSlotNames);
EditorGUILayout.PropertyField(meshes,
new GUIContent("Render MeshAttachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"));
EditorGUILayout.PropertyField(immutableTriangles,
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
EditorGUILayout.Space();
const float MinZSpacing = -0.1f;
const float MaxZSpacing = 0f;
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
const float MinZSpacing = -0.1f;
const float MaxZSpacing = 0f;
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
// Optional fields. May be disabled in SkeletonRenderer.
if (normals != null) {
EditorGUILayout.PropertyField(normals);
EditorGUILayout.PropertyField(tangents);
}
if (frontFacing != null)
EditorGUILayout.PropertyField(frontFacing);
// Optional fields. May be disabled in SkeletonRenderer.
if (normals != null) EditorGUILayout.PropertyField(normals, new GUIContent("Add Normals"));
if (tangents != null) EditorGUILayout.PropertyField(tangents, new GUIContent("Solve Tangents"));
if (frontFacing != null) EditorGUILayout.PropertyField(frontFacing);
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
}
@ -162,7 +155,7 @@ namespace Spine.Unity.Editor {
}
override public void OnInspectorGUI () {
serializedObject.Update();
//serializedObject.Update();
DrawInspectorGUI();
if (serializedObject.ApplyModifiedProperties() ||
(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")

View File

@ -34,8 +34,11 @@ 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; } }
public bool PremultiplyVertexColors { get; set; }
protected bool addNormals;
public bool AddNormals { get { return addNormals; } set { addNormals = value; } }
protected bool addTangents { get; set; }
public bool AddTangents { get { return addTangents; } set { addTangents = value; } }
#endregion
protected float[] attachmentVertexBuffer = new float[8];
@ -43,25 +46,22 @@ namespace Spine.Unity.MeshGeneration {
protected Color32[] meshColors32;
protected Vector2[] meshUVs;
protected bool generateNormals = false;
public bool GenerateNormals {
get { return generateNormals; }
set { generateNormals = value; }
}
Vector3[] meshNormals;
#if SPINE_OPTIONAL_NORMALS
protected Vector3[] meshNormals;
#endif
protected Vector4[] meshTangents;
protected Vector2[] tempTanBuffer;
public void TryAddNormalsTo (Mesh mesh, int targetVertexCount) {
#if SPINE_OPTIONAL_NORMALS
if (generateNormals) {
bool verticesWasResized = this.meshNormals == null || targetVertexCount > meshNormals.Length;
if (addNormals) {
bool verticesWasResized = this.meshNormals == null || meshNormals.Length < targetVertexCount;
if (verticesWasResized) {
this.meshNormals = new Vector3[targetVertexCount];
Vector3 normal = new Vector3(0, 0, -1);
Vector3 fixedNormal = new Vector3(0, 0, -1f);
Vector3[] normals = this.meshNormals;
for (int i = 0; i < targetVertexCount; i++)
normals[i] = normal;
normals[i] = fixedNormal;
}
mesh.normals = this.meshNormals;
@ -69,7 +69,8 @@ namespace Spine.Unity.MeshGeneration {
#endif
}
/// <summary>Ensures the sizes of the passed array references. If they are not the correct size, a new array will be assigned to the references.</summary>
/// <returns><c>true</c>, if a resize occurred, <c>false</c> otherwise.</returns>
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;
@ -97,12 +98,10 @@ namespace Spine.Unity.MeshGeneration {
return submeshBuffersWasResized;
}
/// <summary>
/// Fills vertex arrays.
/// </summary>
/// <summary>Fills Unity vertex data buffers with verts from the Spine Skeleton.</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="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>
@ -112,7 +111,8 @@ namespace Spine.Unity.MeshGeneration {
/// <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) {
/// <param name = "renderMeshes">Include MeshAttachments. If false, it will ignore MeshAttachments.</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, bool renderMeshes = true) {
Color32 color;
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
@ -183,7 +183,7 @@ namespace Spine.Unity.MeshGeneration {
else if (y4 > bmax.y) bmax.y = y4;
vi += 4;
} else {
} else if (renderMeshes) {
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
int meshVertexCount = meshAttachment.worldVerticesLength;
@ -229,22 +229,18 @@ namespace Spine.Unity.MeshGeneration {
}
/// <summary>
/// Fills a submesh triangle buffer array.
/// </summary>
/// <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, bool isLastSubmesh) {
public static void FillTriangles (ref int[] triangleBuffer, Skeleton skeleton, int triangleCount, int firstVertex, int startSlot, int endSlot, bool isLastSubmesh) {
int trianglesCapacity = triangleBuffer.Length;
var tris = triangleBuffer;
int[] tris = triangleBuffer;
// Ensure triangleBuffer size.
if (isLastSubmesh) {
if (trianglesCapacity > triangleCount) {
for (int i = triangleCount; i < trianglesCapacity; i++)
@ -256,45 +252,170 @@ namespace Spine.Unity.MeshGeneration {
triangleBuffer = tris = new int[triangleCount];
}
// 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++) {
for (int i = startSlot, n = endSlot, ti = 0, afv = firstVertex; i < n; i++) {
var attachment = skeletonDrawOrderItems[i].attachment;
// RegionAttachment
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;
tris[ti] = afv;
tris[ti + 1] = afv + 2;
tris[ti + 2] = afv + 1;
tris[ti + 3] = afv + 2;
tris[ti + 4] = afv + 3;
tris[ti + 5] = afv + 1;
ti += 6;
afv += 4;
} else {
int[] attachmentTriangles;
int attachmentVertexCount;
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
attachmentVertexCount = meshAttachment.worldVerticesLength >> 1; // length/2
attachmentTriangles = meshAttachment.triangles;
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
tris[triangleIndex] = afv + attachmentTriangles[ii];
afv += attachmentVertexCount;
}
continue;
}
} // Done adding current submesh triangles
// MeshAttachment
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
int[] attachmentTriangles = meshAttachment.triangles;
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, ti++)
tris[ti] = afv + attachmentTriangles[ii];
afv += meshAttachment.worldVerticesLength >> 1; // length/2;
}
}
}
public static void FillTrianglesQuads (ref int[] triangleBuffer, ref int storedTriangleCount, ref int storedFirstVertex, int instructionsFirstVertex, int instructionTriangleCount, bool isLastSubmesh) {
int trianglesCapacity = triangleBuffer.Length;
if (isLastSubmesh && trianglesCapacity > instructionTriangleCount) {
for (int i = instructionTriangleCount; i < trianglesCapacity; i++)
triangleBuffer[i] = 0;
storedTriangleCount = instructionTriangleCount;
} else if (trianglesCapacity != instructionTriangleCount) {
triangleBuffer = new int[instructionTriangleCount];
storedTriangleCount = 0;
}
// Use stored quad triangles if possible.
int[] tris = triangleBuffer;
if (storedFirstVertex != instructionsFirstVertex || storedTriangleCount < instructionTriangleCount) { //|| storedTriangleCount == 0
storedTriangleCount = instructionTriangleCount;
storedFirstVertex = instructionsFirstVertex;
int afv = instructionsFirstVertex; // attachment first vertex
for (int ti = 0; ti < instructionTriangleCount; ti += 6, afv += 4) {
tris[ti] = afv;
tris[ti + 1] = afv + 2;
tris[ti + 2] = afv + 1;
tris[ti + 3] = afv + 2;
tris[ti + 4] = afv + 3;
tris[ti + 5] = afv + 1;
}
}
}
/// <summary>Creates a UnityEngine.Bounds struct from minimum and maximum value vectors.</summary>
public static Bounds ToBounds (Vector3 boundsMin, Vector3 boundsMax) {
Vector3 size = (boundsMax - boundsMin);
Vector3 center = boundsMin + size * 0.5f;
return new Bounds(center, size);
}
#region TangentSolver2D
// Thanks to contributions from forum user ToddRivers
/// <summary>Step 1 of solving tangents. Ensure you have buffers of the correct size.</summary>
/// <param name="tangentBuffer">Eventual Vector4[] tangent buffer to assign to Mesh.tangents.</param>
/// <param name="tempTanBuffer">Temporary Vector2 buffer for calculating directions.</param>
/// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
public static void SolveTangents2DEnsureSize (ref Vector4[] tangentBuffer, ref Vector2[] tempTanBuffer, int vertexCount) {
if (tangentBuffer == null || tangentBuffer.Length < vertexCount)
tangentBuffer = new Vector4[vertexCount];
if (tempTanBuffer == null || tempTanBuffer.Length < vertexCount * 2)
tempTanBuffer = new Vector2[vertexCount * 2]; // two arrays in one.
}
/// <summary>Step 2 of solving tangents. Fills (part of) a temporary tangent-solution buffer based on the vertices and uvs defined by a submesh's triangle buffer. Only needs to be called once for single-submesh meshes.</summary>
/// <param name="tempTanBuffer">A temporary Vector3[] for calculating tangents.</param>
/// <param name="vertices">The mesh's current vertex position buffer.</param>
/// <param name="triangles">The mesh's current triangles buffer.</param>
/// <param name="uvs">The mesh's current uvs buffer.</param>
/// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
/// <param name = "triangleCount">The number of triangle indexes in the triangle array to be used.</param>
public static void SolveTangents2DTriangles (Vector2[] tempTanBuffer, int[] triangles, int triangleCount, Vector3[] vertices, Vector2[] uvs, int vertexCount) {
Vector2 sdir;
Vector2 tdir;
for (int t = 0; t < triangleCount; t += 3) {
int i1 = triangles[t + 0];
int i2 = triangles[t + 1];
int i3 = triangles[t + 2];
Vector3 v1 = vertices[i1];
Vector3 v2 = vertices[i2];
Vector3 v3 = vertices[i3];
Vector2 w1 = uvs[i1];
Vector2 w2 = uvs[i2];
Vector2 w3 = uvs[i3];
float x1 = v2.x - v1.x;
float x2 = v3.x - v1.x;
float y1 = v2.y - v1.y;
float y2 = v3.y - v1.y;
float s1 = w2.x - w1.x;
float s2 = w3.x - w1.x;
float t1 = w2.y - w1.y;
float t2 = w3.y - w1.y;
float div = s1 * t2 - s2 * t1;
float r = (div == 0f) ? 0f : 1f / div;
sdir.x = (t2 * x1 - t1 * x2) * r;
sdir.y = (t2 * y1 - t1 * y2) * r;
tempTanBuffer[i1] = tempTanBuffer[i2] = tempTanBuffer[i3] = sdir;
tdir.x = (s1 * x2 - s2 * x1) * r;
tdir.y = (s1 * y2 - s2 * y1) * r;
tempTanBuffer[vertexCount + i1] = tempTanBuffer[vertexCount + i2] = tempTanBuffer[vertexCount + i3] = tdir;
}
}
/// <summary>Step 3 of solving tangents. Fills a Vector4[] tangents array according to values calculated in step 2.</summary>
/// <param name="tangents">A Vector4[] that will eventually be used to set Mesh.tangents</param>
/// <param name="tempTanBuffer">A temporary Vector3[] for calculating tangents.</param>
/// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
public static void SolveTangents2DBuffer (Vector4[] tangents, Vector2[] tempTanBuffer, int vertexCount) {
Vector4 tangent;
tangent.z = 0;
for (int i = 0; i < vertexCount; ++i) {
Vector2 t = tempTanBuffer[i];
// t.Normalize() (aggressively inlined). Even better if offloaded to GPU via vertex shader.
float magnitude = Mathf.Sqrt(t.x * t.x + t.y * t.y);
if (magnitude > 1E-05) {
float reciprocalMagnitude = 1f/magnitude;
t.x *= reciprocalMagnitude;
t.y *= reciprocalMagnitude;
}
Vector2 t2 = tempTanBuffer[vertexCount + i];
tangent.x = t.x;
tangent.y = t.y;
//tangent.z = 0;
tangent.w = (t.y * t2.x > t.x * t2.y) ? 1 : -1; // 2D direction calculation. Used for binormals.
tangents[i] = tangent;
}
}
#endregion
#region SubmeshTriangleBuffer
public class SubmeshTriangleBuffer {
public int[] triangles;
//public int triangleCount;
public int triangleCount; // for last/single submeshes with potentially zeroed triangles.
public int firstVertex = -1; // for !renderMeshes.
public SubmeshTriangleBuffer () { }
public SubmeshTriangleBuffer (int triangleCount) {
triangles = new int[triangleCount];

View File

@ -34,18 +34,15 @@ namespace Spine.Unity.MeshGeneration {
public class ArraysSimpleMeshGenerator : ArraysMeshGenerator, ISimpleMeshGenerator {
#region Settings
protected float scale = 1f;
public float Scale {
get { return scale; }
set { scale = value; }
}
public float Scale { get { return scale; } set { scale = value; } }
public float ZSpacing { get; set; }
#endregion
private Mesh lastGeneratedMesh;
protected 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
@ -80,7 +77,6 @@ namespace Spine.Unity.MeshGeneration {
this.triangles = this.triangles ?? new int[totalTriangleCount];
// 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)
Vector3 meshBoundsMin;
Vector3 meshBoundsMax;
@ -96,7 +92,7 @@ namespace Spine.Unity.MeshGeneration {
meshBoundsMax.z = zFauxHalfThickness * scale;
int vertexIndex = 0;
ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, zSpacing, this.premultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, this.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;
@ -111,7 +107,7 @@ namespace Spine.Unity.MeshGeneration {
}
// Step 4 : Update Triangles buffer
ArraysMeshGenerator.FillTriangles(skeleton, totalTriangleCount, 0, 0, drawOrderCount, ref this.triangles, true);
ArraysMeshGenerator.FillTriangles(ref this.triangles, skeleton, totalTriangleCount, 0, 0, drawOrderCount, true);
// Step 5 : Update Mesh with buffers
var mesh = doubleBufferedMesh.GetNextMesh();
@ -120,6 +116,13 @@ namespace Spine.Unity.MeshGeneration {
mesh.uv = meshUVs;
mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
mesh.triangles = triangles;
TryAddNormalsTo(mesh, totalVertexCount);
if (addTangents) {
SolveTangents2DEnsureSize(ref this.meshTangents, ref this.tempTanBuffer, totalVertexCount);
SolveTangents2DTriangles(this.tempTanBuffer, triangles, totalTriangleCount, meshVertices, meshUVs, totalVertexCount);
SolveTangents2DBuffer(this.meshTangents, this.tempTanBuffer, totalVertexCount);
}
lastGeneratedMesh = mesh;
return mesh;

View File

@ -28,13 +28,12 @@
* 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;
namespace Spine.Unity.MeshGeneration {
public class ArraysSubmeshSetMeshGenerator : ArraysMeshGenerator, ISubmeshSetMeshGenerator {
#region Settings
public float zSpacing = 0f;
public float ZSpacing { get; set; }
#endregion
readonly DoubleBuffered<SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
@ -69,7 +68,7 @@ namespace Spine.Unity.MeshGeneration {
// Initial values for manual Mesh Bounds calculation
Vector3 meshBoundsMin;
Vector3 meshBoundsMax;
float zSpacing = this.zSpacing;
float zSpacing = this.ZSpacing;
if (vertexCount <= 0) {
meshBoundsMin = new Vector3(0, 0, 0);
meshBoundsMax = new Vector3(0, 0, 0);
@ -103,7 +102,7 @@ namespace Spine.Unity.MeshGeneration {
var ca = skeletonDrawOrderItems[i].attachment;
if (ca != null) workingAttachments.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);
ArraysMeshGenerator.FillVerts(skeleton, startSlot, endSlot, zSpacing, this.PremultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
}
bool structureDoesntMatch = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(workingAttachments, currentInstructions);
@ -112,7 +111,9 @@ namespace Spine.Unity.MeshGeneration {
if (structureDoesntMatch) {
var currentBuffer = submeshBuffers.Items[submeshIndex];
bool isLastSubmesh = (submeshIndex == submeshCount - 1);
ArraysMeshGenerator.FillTriangles(currentInstruction.skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, currentInstruction.startSlot, currentInstruction.endSlot, ref currentBuffer.triangles, isLastSubmesh);
ArraysMeshGenerator.FillTriangles(ref currentBuffer.triangles, currentInstruction.skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, currentInstruction.startSlot, currentInstruction.endSlot, isLastSubmesh);
currentBuffer.triangleCount = currentInstruction.triangleCount;
currentBuffer.firstVertex = currentInstruction.firstVertexIndex;
}
}
@ -124,15 +125,26 @@ namespace Spine.Unity.MeshGeneration {
// STEP 3: Assign the buffers into the Mesh.
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, workingAttachments, currentInstructions);
mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
#if SPINE_OPTIONAL_NORMALS
this.TryAddNormalsTo(mesh, vertexCount);
#endif
if (structureDoesntMatch) {
// Push new triangles if doesn't match.
mesh.subMeshCount = submeshCount;
for (int i = 0; i < submeshCount; i++)
mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
this.TryAddNormalsTo(mesh, vertexCount);
}
if (addTangents) {
SolveTangents2DEnsureSize(ref this.meshTangents, ref this.tempTanBuffer, vertexCount);
for (int i = 0, n = submeshCount; i < n; i++) {
var submesh = submeshBuffers.Items[i];
SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, meshVertices, meshUVs, vertexCount);
}
SolveTangents2DBuffer(this.meshTangents, this.tempTanBuffer, vertexCount);
}
return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);

View File

@ -28,7 +28,6 @@
* 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;
@ -42,11 +41,9 @@ namespace Spine.Unity.MeshGeneration {
readonly List<Slot> separators = new List<Slot>();
public List<Slot> Separators { get { return this.separators; } }
public float zSpacing = 0f;
#if SPINE_OPTIONAL_NORMALS
public bool generateNormals;
public bool generateTangents;
#endif
#region Settings
public float ZSpacing { get; set; }
#endregion
readonly DoubleBuffered<SmartMesh> doubleBufferedSmartMesh = new DoubleBuffered<SmartMesh>();
readonly SubmeshedMeshInstruction currentInstructions = new SubmeshedMeshInstruction();
@ -158,12 +155,13 @@ namespace Spine.Unity.MeshGeneration {
var instructionList = meshInstructions.submeshInstructions;
// STEP 1: Ensure correct buffer sizes.
int vertexCount = meshInstructions.vertexCount;
bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, instructionList.Items);
bool vertBufferResized = ArraysMeshGenerator.EnsureSize(meshInstructions.vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
bool vertBufferResized = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
Vector3[] vertices = this.meshVertices;
// STEP 2: Update buffers based on Skeleton.
float zSpacing = this.zSpacing;
float zSpacing = this.ZSpacing;
Vector3 meshBoundsMin;
Vector3 meshBoundsMax;
int attachmentCount = meshInstructions.attachmentList.Count;
@ -192,11 +190,13 @@ namespace Spine.Unity.MeshGeneration {
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);
ArraysMeshGenerator.FillVerts(skeleton, start, end, zSpacing, this.PremultiplyVertexColors, vertices, 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, submeshInstruction.triangleCount, submeshInstruction.firstVertexIndex, start, end, ref currentBuffer.triangles, isLastSubmesh);
ArraysMeshGenerator.FillTriangles(ref currentBuffer.triangles, skeleton, submeshInstruction.triangleCount, submeshInstruction.firstVertexIndex, start, end, isLastSubmesh);
currentBuffer.triangleCount = submeshInstruction.triangleCount;
currentBuffer.firstVertex = submeshInstruction.firstVertexIndex;
}
}
@ -215,24 +215,16 @@ namespace Spine.Unity.MeshGeneration {
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;
TryAddNormalsTo(mesh, vertexCount);
}
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;
}
if (addTangents) {
SolveTangents2DEnsureSize(ref this.meshTangents, ref this.tempTanBuffer, vertexCount);
for (int i = 0, n = submeshCount; i < n; i++) {
var submesh = submeshBuffers.Items[i];
SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, meshVertices, meshUVs, vertexCount);
}
#endif
SolveTangents2DBuffer(this.meshTangents, this.tempTanBuffer, vertexCount);
}
return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);

View File

@ -4,8 +4,13 @@
// The Scale property allows generated mesh to match external systems like Canvas referencePixelsPerUnit
public interface ISimpleMeshGenerator {
float Scale { set; }
UnityEngine.Mesh GenerateMesh (Spine.Skeleton skeleton);
UnityEngine.Mesh LastGeneratedMesh { get; }
float Scale { set; }
float ZSpacing { get; set; }
bool AddNormals { get; set; }
bool AddTangents { get; set; }
}
}

View File

@ -12,6 +12,10 @@ namespace Spine.Unity.MeshGeneration {
SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton);
MeshAndMaterials GenerateMesh (SubmeshedMeshInstruction wholeMeshInstruction);
List<Slot> Separators { get; }
float ZSpacing { get; set; }
bool AddNormals { get; set; }
bool AddTangents { get; set; }
}
// ISubmeshSetMeshGenerator
@ -22,7 +26,10 @@ namespace Spine.Unity.MeshGeneration {
// Step 4: Put the Mesh in MeshFilter. Put the Materials in MeshRenderer.sharedMaterials.
public interface ISubmeshSetMeshGenerator {
MeshAndMaterials GenerateMesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh);
bool GenerateNormals { get; set; }
float ZSpacing { get; set; }
bool AddNormals { get; set; }
bool AddTangents { get; set; }
}
/// <summary>Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes.</summary>

View File

@ -69,7 +69,7 @@ namespace Spine.Unity.Modules {
void OnEnable () {
if (skeletonRenderer == null) return;
if (block == null) block = new MaterialPropertyBlock();
if (copiedBlock == null) copiedBlock = new MaterialPropertyBlock();
mainMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
skeletonRenderer.GenerateMeshOverride -= HandleRender;
@ -103,7 +103,7 @@ namespace Spine.Unity.Modules {
s.ClearMesh();
}
MaterialPropertyBlock block;
MaterialPropertyBlock copiedBlock;
void HandleRender (SkeletonRenderer.SmartMesh.Instruction instruction) {
int rendererCount = partsRenderers.Count;
@ -112,21 +112,27 @@ namespace Spine.Unity.Modules {
int rendererIndex = 0;
if (copyPropertyBlock)
mainMeshRenderer.GetPropertyBlock(block);
mainMeshRenderer.GetPropertyBlock(copiedBlock);
var submeshInstructions = instruction.submeshInstructions;
var submeshInstructionsItems = submeshInstructions.Items;
int lastSubmeshInstruction = submeshInstructions.Count - 1;
var currentRenderer = partsRenderers[rendererIndex];
bool useNormals = skeletonRenderer.calculateNormals;
bool addNormals = skeletonRenderer.calculateNormals;
bool addTangents = skeletonRenderer.calculateTangents;
for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) {
if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) {
currentRenderer.RenderParts(instruction.submeshInstructions, start, si + 1);
currentRenderer.MeshGenerator.GenerateNormals = useNormals;
// Apply properties
var meshGenerator = currentRenderer.MeshGenerator;
meshGenerator.AddNormals = addNormals;
meshGenerator.AddTangents = addTangents;
if (copyPropertyBlock)
currentRenderer.SetPropertyBlock(block);
currentRenderer.SetPropertyBlock(copiedBlock);
// Render
currentRenderer.RenderParts(instruction.submeshInstructions, start, si + 1);
start = si + 1;
rendererIndex++;

View File

@ -0,0 +1,6 @@
SkeletonRenderSeparator
=======================
Dependencies:
- SkeletonPartsRenderer uses the `ArraysMeshGenerator` class in `Spine.Unity.MeshGeneration`
- It requires `SPINE_OPTIONAL_RENDEROVERRIDE` to be #defined in `SkeletonRenderer.cs`.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f0e413eeb00eabc46bde6dbd7aaaa76c
timeCreated: 1469110129
licenseType: Free
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -28,9 +28,12 @@
* 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_RENDEROVERRIDE
#define SPINE_OPTIONAL_MATERIALOVERRIDE
#define SPINE_OPTIONAL_NORMALS
#define SPINE_OPTIONAL_SOLVETANGENTS
//#define SPINE_OPTIONAL_FRONTFACING
//#define SPINE_OPTIONAL_SUBMESHRENDERER // Deprecated
@ -62,8 +65,12 @@ namespace Spine.Unity {
public float zSpacing;
public bool renderMeshes = true, immutableTriangles;
public bool pmaVertexColors = true;
#if SPINE_OPTIONAL_NORMALS
public bool calculateNormals, calculateTangents;
public bool calculateNormals;
#endif
#if SPINE_OPTIONAL_SOLVETANGENTS
public bool calculateTangents;
#endif
#if SPINE_OPTIONAL_FRONTFACING
public bool frontFacing;
@ -115,7 +122,7 @@ namespace Spine.Unity {
Spine.Unity.DoubleBuffered<SkeletonRenderer.SmartMesh> doubleBufferedMesh;
readonly SmartMesh.Instruction currentInstructions = new SmartMesh.Instruction();
readonly ExposedList<SubmeshTriangleBuffer> submeshes = new ExposedList<SubmeshTriangleBuffer>();
readonly ExposedList<ArraysMeshGenerator.SubmeshTriangleBuffer> submeshes = new ExposedList<ArraysMeshGenerator.SubmeshTriangleBuffer>();
readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
Material[] sharedMaterials = new Material[0];
float[] tempVertices = new float[8];
@ -124,7 +131,10 @@ namespace Spine.Unity {
Vector2[] uvs;
#if SPINE_OPTIONAL_NORMALS
Vector3[] normals;
#endif
#if SPINE_OPTIONAL_SOLVETANGENTS
Vector4[] tangents;
Vector2[] tempTanBuffer;
#endif
#region Runtime Instantiation
@ -189,7 +199,7 @@ namespace Spine.Unity {
vertices = new Vector3[0];
skeleton = new Skeleton(skeletonData);
if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default")
if (!string.IsNullOrEmpty(initialSkinName) && initialSkinName != "default")
skeleton.SetSkin(initialSkinName);
separatorSlots.Clear();
@ -211,27 +221,18 @@ namespace Spine.Unity {
return;
if (
(
!meshRenderer.enabled
)
(!meshRenderer.enabled)
#if SPINE_OPTIONAL_RENDEROVERRIDE
&& this.generateMeshOverride == null
#endif
#if SPINE_OPTIONAL_SUBMESHRENDERER
&& submeshRenderers.Length > 0
#endif
)
return;
// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes.
// This method caches several .Items arrays.
// Never mutate their overlying ExposedList objects.
// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. ============================================================
ExposedList<Slot> drawOrder = skeleton.drawOrder;
var drawOrderItems = drawOrder.Items;
int drawOrderCount = drawOrder.Count;
@ -267,7 +268,6 @@ namespace Spine.Unity {
for (int i = 0; i < drawOrderCount; i++) {
Slot slot = drawOrderItems[i];
Attachment attachment = slot.attachment;
workingAttachmentsItems[i] = attachment;
#if SPINE_OPTIONAL_FRONTFACING
@ -357,7 +357,8 @@ namespace Spine.Unity {
workingInstruction.frontFacing = this.frontFacing;
#endif
// STEP 1.9. Post-process workingInstructions.
// STEP 1.9. Post-process workingInstructions. ============================================================
#if SPINE_OPTIONAL_MATERIALOVERRIDE
// Material overrides are done here so they can be applied per submesh instead of per slot
@ -374,7 +375,6 @@ namespace Spine.Unity {
}
}
#endif
#if SPINE_OPTIONAL_RENDEROVERRIDE
if (this.generateMeshOverride != null) {
this.generateMeshOverride(workingInstruction);
@ -382,50 +382,22 @@ namespace Spine.Unity {
}
#endif
// 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;
if (vertexCountIncreased) {
this.vertices = vertices = new Vector3[vertexCount];
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);
for (int i = 0; i < vertexCount; i++)
localNormals[i] = normal;
}
// For dynamic tangent calculation, you can remove the tangent-filling logic and add tangent calculation logic below.
if (calculateTangents) {
Vector4[] localTangents = this.tangents = new Vector4[vertexCount];
Vector4 tangent = new Vector4(1, 0, 0, -1);
for (int i = 0; i < vertexCount; i++)
localTangents[i] = tangent;
}
#endif
} else {
Vector3 zero = Vector3.zero;
for (int i = vertexCount, n = vertices.Length; i < n; i++)
vertices[i] = zero;
bool vertexCountIncreased = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.vertices, ref this.uvs, ref this.colors);
#if SPINE_OPTIONAL_NORMALS
if (vertexCountIncreased && calculateNormals) {
Vector3[] localNormals = this.normals = new Vector3[vertexCount];
Vector3 normal = new Vector3(0, 0, -1);
for (int i = 0; i < vertexCount; i++)
localNormals[i] = normal;
}
float zSpacing = this.zSpacing;
float[] tempVertices = this.tempVertices;
Vector2[] uvs = this.uvs;
Color32[] colors = this.colors;
int vertexIndex = 0;
bool pmaVertexColors = this.pmaVertexColors;
Color32 color;
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
#endif
Vector3 meshBoundsMin;
Vector3 meshBoundsMax;
if (vertexCount == 0) {
if (vertexCount <= 0) {
meshBoundsMin = new Vector3(0, 0, 0);
meshBoundsMax = new Vector3(0, 0, 0);
} else {
@ -433,6 +405,7 @@ namespace Spine.Unity {
meshBoundsMin.y = int.MaxValue;
meshBoundsMax.x = int.MinValue;
meshBoundsMax.y = int.MinValue;
if (zSpacing > 0f) {
meshBoundsMin.z = 0f;
meshBoundsMax.z = zSpacing * (drawOrderCount - 1);
@ -440,192 +413,60 @@ namespace Spine.Unity {
meshBoundsMin.z = zSpacing * (drawOrderCount - 1);
meshBoundsMax.z = 0f;
}
int i = 0;
do {
Slot slot = drawOrderItems[i];
Attachment attachment = slot.attachment;
RegionAttachment regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
regionAttachment.ComputeWorldVertices(slot.bone, tempVertices);
float z = i * zSpacing;
float x1 = tempVertices[RegionAttachment.X1], y1 = tempVertices[RegionAttachment.Y1];
float x2 = tempVertices[RegionAttachment.X2], y2 = tempVertices[RegionAttachment.Y2];
float x3 = tempVertices[RegionAttachment.X3], y3 = tempVertices[RegionAttachment.Y3];
float x4 = tempVertices[RegionAttachment.X4], y4 = tempVertices[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 (pmaVertexColors) {
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[vertexIndex] = color;
colors[vertexIndex + 1] = color;
colors[vertexIndex + 2] = color;
colors[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 {
if (!renderMeshes)
continue;
MeshAttachment meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
int meshVertexCount = meshAttachment.worldVerticesLength;
if (tempVertices.Length < meshVertexCount)
this.tempVertices = tempVertices = new float[meshVertexCount];
meshAttachment.ComputeWorldVertices(slot, tempVertices);
if (pmaVertexColors) {
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[] meshUVs = meshAttachment.uvs;
float z = i * zSpacing;
for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) {
float x = tempVertices[ii], y = tempVertices[ii + 1];
vertices[vertexIndex].x = x;
vertices[vertexIndex].y = y;
vertices[vertexIndex].z = z;
colors[vertexIndex] = color;
uvs[vertexIndex].x = meshUVs[ii];
uvs[vertexIndex].y = meshUVs[ii + 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;
}
}
}
} while (++i < drawOrderCount);
}
int vertexIndex = 0;
ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, this.zSpacing, pmaVertexColors, this.vertices, this.uvs, this.colors, ref vertexIndex, ref tempVertices, ref meshBoundsMin, ref meshBoundsMax, renderMeshes);
// Step 3. Move the mesh data into a UnityEngine.Mesh
// Step 3. Move the mesh data into a UnityEngine.Mesh ============================================================
var currentSmartMesh = doubleBufferedMesh.GetNext(); // Double-buffer for performance.
var currentMesh = currentSmartMesh.mesh;
currentMesh.vertices = vertices;
currentMesh.vertices = this.vertices;
currentMesh.colors32 = colors;
currentMesh.uv = uvs;
Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin;
Vector3 meshBoundsCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
currentMesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents);
currentMesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
var currentSmartMeshInstructionUsed = currentSmartMesh.instructionUsed;
#if SPINE_OPTIONAL_NORMALS
if (currentSmartMeshInstructionUsed.vertexCount < vertexCount) {
if (calculateNormals)
currentMesh.normals = normals;
// For dynamic calculated tangents, this needs to be moved out of the vertexCount check block when replacing the logic, also ensuring the size.
if (calculateTangents)
currentMesh.tangents = this.tangents;
}
if (calculateNormals && currentSmartMeshInstructionUsed.vertexCount < vertexCount)
currentMesh.normals = normals;
#endif
// Check if the triangles should also be updated.
// This thorough structure check is cheaper than updating triangles every frame.
bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(workingInstruction, currentSmartMeshInstructionUsed);
int submeshCount = workingSubmeshInstructions.Count;
if (mustUpdateMeshStructure) {
var thisSubmeshMaterials = this.submeshMaterials;
thisSubmeshMaterials.Clear(false);
int submeshCount = workingSubmeshInstructions.Count;
int oldSubmeshCount = submeshes.Count;
submeshes.Capacity = submeshCount;
for (int i = oldSubmeshCount; i < submeshCount; i++)
submeshes.Items[i] = new SubmeshTriangleBuffer();
submeshes.Items[i] = new ArraysMeshGenerator.SubmeshTriangleBuffer(workingSubmeshInstructions.Items[i].triangleCount);
var mutableTriangles = !workingInstruction.immutableTriangles;
for (int i = 0, last = submeshCount - 1; i < submeshCount; i++) {
var submeshInstruction = workingSubmeshInstructions.Items[i];
if (mutableTriangles || i >= oldSubmeshCount)
SetSubmesh(i, submeshInstruction,
#if SPINE_OPTIONAL_FRONTFACING
currentInstructions.attachmentFlips,
#endif
i == last);
if (mutableTriangles || i >= oldSubmeshCount) {
#if !SPINE_OPTIONAL_FRONTFACING
var currentSubmesh = submeshes.Items[i];
int instructionTriangleCount = submeshInstruction.triangleCount;
if (renderMeshes) {
ArraysMeshGenerator.FillTriangles(ref currentSubmesh.triangles, skeleton, instructionTriangleCount, submeshInstruction.firstVertexIndex, submeshInstruction.startSlot, submeshInstruction.endSlot, (i == last));
currentSubmesh.triangleCount = instructionTriangleCount;
} else {
ArraysMeshGenerator.FillTrianglesQuads(ref currentSubmesh.triangles, ref currentSubmesh.triangleCount, ref currentSubmesh.firstVertex, submeshInstruction.firstVertexIndex, instructionTriangleCount, (i == last));
}
#else
SetSubmesh(i, submeshInstruction, currentInstructions.attachmentFlips, i == last);
#endif
}
thisSubmeshMaterials.Add(submeshInstruction.material);
}
@ -635,12 +476,24 @@ namespace Spine.Unity {
currentMesh.SetTriangles(submeshes.Items[i].triangles, i);
}
#if SPINE_OPTIONAL_SOLVETANGENTS
if (calculateTangents) {
ArraysMeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertices.Length);
for (int i = 0; i < submeshCount; i++) {
var submesh = submeshes.Items[i];
ArraysMeshGenerator.SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, this.vertices, this.uvs, vertexCount);
}
ArraysMeshGenerator.SolveTangents2DBuffer(this.tangents, this.tempTanBuffer, vertexCount);
currentMesh.tangents = this.tangents;
}
#endif
// CheckIfMustUpdateMaterialArray (last pushed materials vs currently parsed materials)
// Needs to check against the Working Submesh Instructions Materials instead of the cached submeshMaterials.
{
var lastPushedMaterials = this.sharedMaterials;
bool mustUpdateRendererMaterials = mustUpdateMeshStructure ||
(lastPushedMaterials.Length != workingSubmeshInstructions.Count);
(lastPushedMaterials.Length != submeshCount);
if (!mustUpdateRendererMaterials) {
var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items;
@ -662,14 +515,12 @@ namespace Spine.Unity {
}
}
// Step 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh.
// Step 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. ============================================================
meshFilter.sharedMesh = currentMesh;
currentSmartMesh.instructionUsed.Set(workingInstruction);
// Step 5. Miscellaneous
// Add stuff here if you want
#if SPINE_OPTIONAL_SUBMESHRENDERER
if (submeshRenderers.Length > 0) {
for (int i = 0; i < submeshRenderers.Length; i++) {
@ -748,10 +599,7 @@ namespace Spine.Unity {
#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];
var currentSubmesh = submeshes.Items[submeshIndex];
int[] triangles = currentSubmesh.triangles;
int triangleCount = submeshInstructions.triangleCount;
@ -770,12 +618,8 @@ namespace Spine.Unity {
currentSubmesh.triangles = triangles = new int[triangleCount];
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;
@ -792,20 +636,14 @@ namespace Spine.Unity {
}
return;
}
// 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;
int triangleIndex = 0; // Modified by loop
for (int i = submeshInstructions.startSlot, n = submeshInstructions.endSlot; i < n; i++) {
Attachment attachment = drawOrderItems[i].attachment;
#if SPINE_OPTIONAL_FRONTFACING
bool flip = frontFacing && flipStatesItems[i];
// Add RegionAttachment triangles
@ -830,20 +668,6 @@ namespace Spine.Unity {
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;
@ -856,7 +680,6 @@ namespace Spine.Unity {
continue;
}
#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];
@ -868,14 +691,11 @@ namespace Spine.Unity {
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;
}
}
#endif
#if UNITY_EDITOR
void OnDrawGizmos () {
@ -943,13 +763,5 @@ namespace Spine.Unity {
}
}
}
class SubmeshTriangleBuffer {
public int[] triangles = new int[0];
// These two fields are used when renderMeshes == false
public int triangleCount;
public int firstVertex = -1;
}
}
}