mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Multiple submeshes per part.
This commit is contained in:
parent
852102ab8f
commit
8e8b5e3904
@ -0,0 +1,149 @@
|
|||||||
|
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,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d8742ee1d54c9954d95c9e6508806ac2
|
guid: 043e864df13214e4989d29c17a863b08
|
||||||
timeCreated: 1457405832
|
timeCreated: 1458056200
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@ -32,7 +32,7 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
|
||||||
namespace Spine.Unity {
|
namespace Spine.Unity.MeshGeneration {
|
||||||
public class ArraysSimpleMeshGenerator : ISimpleMeshGenerator {
|
public class ArraysSimpleMeshGenerator : ISimpleMeshGenerator {
|
||||||
#region Settings
|
#region Settings
|
||||||
protected float scale = 1f;
|
protected float scale = 1f;
|
||||||
@ -42,6 +42,8 @@ namespace Spine.Unity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool renderMeshes = true;
|
public bool renderMeshes = true;
|
||||||
|
|
||||||
|
public bool premultiplyVertexColors = true;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Buffers
|
#region Buffers
|
||||||
@ -112,14 +114,11 @@ namespace Spine.Unity {
|
|||||||
|
|
||||||
// Step 3 : Push vertices to arrays
|
// Step 3 : Push vertices to arrays
|
||||||
//
|
//
|
||||||
const float z = 0;
|
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)
|
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;
|
float[] tempVertices = this.tempVertices;
|
||||||
Vector2[] uvs = this.uvs;
|
Vector2[] uvs = this.uvs;
|
||||||
Color32[] colors = this.colors;
|
Color32[] colors = this.colors;
|
||||||
int vertexIndex = 0;
|
|
||||||
Color32 color;
|
|
||||||
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
|
|
||||||
|
|
||||||
Vector3 meshBoundsMin;
|
Vector3 meshBoundsMin;
|
||||||
Vector3 meshBoundsMax;
|
Vector3 meshBoundsMax;
|
||||||
@ -134,116 +133,23 @@ namespace Spine.Unity {
|
|||||||
meshBoundsMin.z = -zFauxHalfThickness;
|
meshBoundsMin.z = -zFauxHalfThickness;
|
||||||
meshBoundsMax.z = zFauxHalfThickness;
|
meshBoundsMax.z = zFauxHalfThickness;
|
||||||
|
|
||||||
int i = 0;
|
int vertexIndex = 0;
|
||||||
do {
|
ArraysBuffers.Fill(skeleton, 0, drawOrderCount, zSpacing, this.premultiplyVertexColors, vertices, uvs, colors, ref vertexIndex, ref tempVertices, ref meshBoundsMin, ref meshBoundsMax);
|
||||||
Slot slot = drawOrderItems[i];
|
this.tempVertices = tempVertices;
|
||||||
Attachment attachment = slot.attachment;
|
|
||||||
var regionAttachment = attachment as RegionAttachment;
|
|
||||||
if (regionAttachment != null) {
|
|
||||||
regionAttachment.ComputeWorldVertices(slot.bone, tempVertices);
|
|
||||||
|
|
||||||
float x1 = tempVertices[RegionAttachment.X1], y1 = tempVertices[RegionAttachment.Y1];
|
// Apply scale to vertices
|
||||||
float x2 = tempVertices[RegionAttachment.X2], y2 = tempVertices[RegionAttachment.Y2];
|
for (int i = 0; i < totalVertexCount; i++) {
|
||||||
float x3 = tempVertices[RegionAttachment.X3], y3 = tempVertices[RegionAttachment.Y3];
|
var v = vertices[i];
|
||||||
float x4 = tempVertices[RegionAttachment.X4], y4 = tempVertices[RegionAttachment.Y4];
|
v.x *= scale;
|
||||||
vertices[vertexIndex].x = x1 * scale; vertices[vertexIndex].y = y1 * scale; vertices[vertexIndex].z = z;
|
v.y *= scale;
|
||||||
vertices[vertexIndex + 1].x = x4 * scale; vertices[vertexIndex + 1].y = y4 * scale; vertices[vertexIndex + 1].z = z;
|
vertices[i] = v;
|
||||||
vertices[vertexIndex + 2].x = x2 * scale; vertices[vertexIndex + 2].y = y2 * scale; vertices[vertexIndex + 2].z = z;
|
}
|
||||||
vertices[vertexIndex + 3].x = x3 * scale; vertices[vertexIndex + 3].y = y3 * scale; vertices[vertexIndex + 3].z = z;
|
|
||||||
|
|
||||||
color.a = (byte)(a * slot.a * regionAttachment.a);
|
meshBoundsMax.x *= scale;
|
||||||
color.r = (byte)(r * slot.r * regionAttachment.r * color.a);
|
meshBoundsMax.y *= scale;
|
||||||
color.g = (byte)(g * slot.g * regionAttachment.g * color.a);
|
meshBoundsMin.x *= scale;
|
||||||
color.b = (byte)(b * slot.b * regionAttachment.b * color.a);
|
meshBoundsMax.y *= scale;
|
||||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
|
||||||
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;
|
|
||||||
var meshAttachment = attachment as MeshAttachment;
|
|
||||||
if (meshAttachment != null) {
|
|
||||||
int meshVertexCount = meshAttachment.vertices.Length;
|
|
||||||
if (tempVertices.Length < meshVertexCount)
|
|
||||||
this.tempVertices = tempVertices = new float[meshVertexCount];
|
|
||||||
meshAttachment.ComputeWorldVertices(slot, tempVertices);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
float[] meshUVs = meshAttachment.uvs;
|
|
||||||
for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) {
|
|
||||||
float x = tempVertices[ii], y = tempVertices[ii + 1];
|
|
||||||
vertices[vertexIndex].x = x * scale; vertices[vertexIndex].y = y * scale; 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;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
|
|
||||||
if (skinnedMeshAttachment != null) {
|
|
||||||
int meshVertexCount = skinnedMeshAttachment.uvs.Length;
|
|
||||||
if (tempVertices.Length < meshVertexCount)
|
|
||||||
this.tempVertices = tempVertices = new float[meshVertexCount];
|
|
||||||
skinnedMeshAttachment.ComputeWorldVertices(slot, tempVertices);
|
|
||||||
|
|
||||||
color.a = (byte)(a * slot.a * skinnedMeshAttachment.a);
|
|
||||||
color.r = (byte)(r * slot.r * skinnedMeshAttachment.r * color.a);
|
|
||||||
color.g = (byte)(g * slot.g * skinnedMeshAttachment.g * color.a);
|
|
||||||
color.b = (byte)(b * slot.b * skinnedMeshAttachment.b * color.a);
|
|
||||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
|
||||||
|
|
||||||
float[] meshUVs = skinnedMeshAttachment.uvs;
|
|
||||||
for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) {
|
|
||||||
float x = tempVertices[ii], y = tempVertices[ii + 1];
|
|
||||||
vertices[vertexIndex].x = x * scale; vertices[vertexIndex].y = y * scale; 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,185 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,394 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
using Spine;
|
||||||
|
|
||||||
|
namespace Spine.Unity.MeshGeneration {
|
||||||
|
public class ArraysSubmeshSetMeshGenerator : ISubmeshSetMeshGenerator {
|
||||||
|
public float zSpacing = 0f;
|
||||||
|
public bool premultiplyVertexColors = true;
|
||||||
|
|
||||||
|
public MeshAndMaterials GenerateMesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} 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 * (currentInstructionsItems[submeshCount - 1].endSlot);
|
||||||
|
} else {
|
||||||
|
meshBoundsMin.z = zSpacing * (currentInstructionsItems[submeshCount - 1].endSlot);
|
||||||
|
meshBoundsMax.z = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool structureDoesntMatch = newVertices || newSubmeshBuffers || smartMesh.StructureDoesntMatch(currentAttachments, currentInstructions);
|
||||||
|
|
||||||
|
if (structureDoesntMatch) {
|
||||||
|
mesh.Clear();
|
||||||
|
|
||||||
|
if (submeshCount == sharedMaterials.Length)
|
||||||
|
currentInstructions.FillMaterialArray(this.sharedMaterials);
|
||||||
|
else
|
||||||
|
this.sharedMaterials = currentInstructions.GetNewMaterialArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, currentAttachments, currentInstructions);
|
||||||
|
mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
public readonly Mesh mesh = SpineMesh.NewMesh();
|
||||||
|
readonly ExposedList<Attachment> attachmentsUsed = new ExposedList<Attachment>();
|
||||||
|
readonly ExposedList<SubmeshInstruction> instructionsUsed = new ExposedList<SubmeshInstruction>();
|
||||||
|
|
||||||
|
public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, ExposedList<Attachment> attachments, ExposedList<SubmeshInstruction> instructions) {
|
||||||
|
mesh.vertices = verts;
|
||||||
|
mesh.uv = uvs;
|
||||||
|
mesh.colors32 = colors;
|
||||||
|
|
||||||
|
attachmentsUsed.Clear(false);
|
||||||
|
attachmentsUsed.GrowIfNeeded(attachments.Capacity);
|
||||||
|
attachmentsUsed.Count = attachments.Count;
|
||||||
|
attachments.CopyTo(attachmentsUsed.Items);
|
||||||
|
|
||||||
|
instructionsUsed.Clear(false);
|
||||||
|
instructionsUsed.GrowIfNeeded(instructions.Capacity);
|
||||||
|
instructionsUsed.Count = instructions.Count;
|
||||||
|
instructions.CopyTo(instructionsUsed.Items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StructureDoesntMatch (ExposedList<Attachment> attachments, ExposedList<SubmeshInstruction> instructions) {
|
||||||
|
// Check count inequality.
|
||||||
|
if (attachments.Count != this.attachmentsUsed.Count) return true;
|
||||||
|
if (instructions.Count != this.instructionsUsed.Count) return true;
|
||||||
|
|
||||||
|
// Check each attachment.
|
||||||
|
var attachmentsPassed = attachments.Items;
|
||||||
|
var myAttachments = this.attachmentsUsed.Items;
|
||||||
|
for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
|
||||||
|
if (attachmentsPassed[i] != myAttachments[i]) return true;
|
||||||
|
|
||||||
|
// Check each submesh for equal arrangement.
|
||||||
|
var instructionListItems = instructions.Items;
|
||||||
|
var myInstructions = this.instructionsUsed.Items;
|
||||||
|
for (int i = 0, n = this.instructionsUsed.Count; i < n; i++) {
|
||||||
|
var lhs = instructionListItems[i];
|
||||||
|
var rhs = myInstructions[i];
|
||||||
|
if (
|
||||||
|
lhs.material.GetInstanceID() != rhs.material.GetInstanceID() ||
|
||||||
|
lhs.startSlot != rhs.startSlot ||
|
||||||
|
lhs.endSlot != rhs.endSlot ||
|
||||||
|
lhs.triangleCount != rhs.triangleCount ||
|
||||||
|
lhs.vertexCount != rhs.vertexCount ||
|
||||||
|
lhs.firstVertexIndex != rhs.firstVertexIndex
|
||||||
|
) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Debug.Log("structure matched");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 5273e5c60bd84a44db5a4e8f3252eef0
|
guid: 11ba077d7d984814db31d054192be532
|
||||||
timeCreated: 1457396525
|
timeCreated: 1458047178
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@ -0,0 +1,365 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Spine;
|
||||||
|
using Spine.Unity;
|
||||||
|
|
||||||
|
namespace Spine.Unity.MeshGeneration {
|
||||||
|
public class ArraysSubmeshedMeshGenerator : ISubmeshedMeshGenerator {
|
||||||
|
|
||||||
|
readonly List<Slot> separators = new List<Slot>();
|
||||||
|
public List<Slot> Separators { get { return this.separators; } }
|
||||||
|
|
||||||
|
public bool premultiplyVertexColors = true;
|
||||||
|
public float zSpacing = 0f;
|
||||||
|
|
||||||
|
// public bool generateNormals;
|
||||||
|
// public bool generateTangents;
|
||||||
|
|
||||||
|
public SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton) {
|
||||||
|
if (skeleton == null) throw new System.ArgumentNullException("skeleton");
|
||||||
|
|
||||||
|
// Count vertices and submesh triangles.
|
||||||
|
int runningVertexCount = 0;
|
||||||
|
|
||||||
|
int submeshTriangleCount = 0;
|
||||||
|
int submeshFirstVertex = 0;
|
||||||
|
int submeshVertexCount = 0;
|
||||||
|
int submeshStartSlotIndex = 0;
|
||||||
|
Material lastMaterial = null;
|
||||||
|
|
||||||
|
var drawOrder = skeleton.drawOrder;
|
||||||
|
var drawOrderItems = drawOrder.Items;
|
||||||
|
int drawOrderCount = drawOrder.Count;
|
||||||
|
int separatorCount = separators.Count;
|
||||||
|
|
||||||
|
var instructionList = this.currentInstructions.submeshInstructions;
|
||||||
|
instructionList.Clear(false);
|
||||||
|
|
||||||
|
currentInstructions.attachmentList.Clear(false);
|
||||||
|
|
||||||
|
for (int i = 0; i < drawOrderCount; i++) {
|
||||||
|
var slot = drawOrderItems[i];
|
||||||
|
var attachment = slot.attachment;
|
||||||
|
|
||||||
|
object rendererObject; // An AtlasRegion in plain Spine-Unity. eventual source of Material object.
|
||||||
|
int attachmentVertexCount, attachmentTriangleCount;
|
||||||
|
|
||||||
|
var regionAttachment = attachment as RegionAttachment;
|
||||||
|
if (regionAttachment != null) {
|
||||||
|
rendererObject = regionAttachment.RendererObject;
|
||||||
|
attachmentVertexCount = 4;
|
||||||
|
attachmentTriangleCount = 6;
|
||||||
|
} else {
|
||||||
|
var meshAttachment = attachment as MeshAttachment;
|
||||||
|
if (meshAttachment != null) {
|
||||||
|
rendererObject = meshAttachment.RendererObject;
|
||||||
|
attachmentVertexCount = meshAttachment.vertices.Length >> 1;
|
||||||
|
attachmentTriangleCount = meshAttachment.triangles.Length;
|
||||||
|
} else {
|
||||||
|
var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
|
||||||
|
if (skinnedMeshAttachment != null) {
|
||||||
|
rendererObject = skinnedMeshAttachment.RendererObject;
|
||||||
|
attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
|
||||||
|
attachmentTriangleCount = skinnedMeshAttachment.triangles.Length;
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) )) {
|
||||||
|
|
||||||
|
instructionList.Add(
|
||||||
|
new SubmeshInstruction {
|
||||||
|
skeleton = skeleton,
|
||||||
|
material = lastMaterial,
|
||||||
|
triangleCount = submeshTriangleCount,
|
||||||
|
vertexCount = submeshVertexCount,
|
||||||
|
startSlot = submeshStartSlotIndex,
|
||||||
|
endSlot = i,
|
||||||
|
firstVertexIndex = submeshFirstVertex
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prepare for next submesh
|
||||||
|
submeshTriangleCount = 0;
|
||||||
|
submeshVertexCount = 0;
|
||||||
|
submeshFirstVertex = runningVertexCount;
|
||||||
|
submeshStartSlotIndex = i;
|
||||||
|
}
|
||||||
|
lastMaterial = attachmentMaterial;
|
||||||
|
|
||||||
|
submeshTriangleCount += attachmentTriangleCount;
|
||||||
|
submeshVertexCount += attachmentVertexCount;
|
||||||
|
runningVertexCount += attachmentVertexCount;
|
||||||
|
|
||||||
|
currentInstructions.attachmentList.Add(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
instructionList.Add(
|
||||||
|
new SubmeshInstruction {
|
||||||
|
skeleton = skeleton,
|
||||||
|
material = lastMaterial,
|
||||||
|
triangleCount = submeshTriangleCount,
|
||||||
|
vertexCount = submeshVertexCount,
|
||||||
|
startSlot = submeshStartSlotIndex,
|
||||||
|
endSlot = drawOrderCount,
|
||||||
|
firstVertexIndex = submeshFirstVertex
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
currentInstructions.vertexCount = runningVertexCount;
|
||||||
|
return currentInstructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 meshBoundsMin;
|
||||||
|
Vector3 meshBoundsMax;
|
||||||
|
|
||||||
|
int attachmentCount = meshInstructions.attachmentList.Count;
|
||||||
|
|
||||||
|
// Initial values for manual Mesh Bounds calculation
|
||||||
|
if (meshInstructions.attachmentList.Count <= 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.attachmentVertexBuffer = attVertBuffer;
|
||||||
|
Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin);
|
||||||
|
Vector3 meshCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
|
||||||
|
|
||||||
|
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, meshInstructions);
|
||||||
|
mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
|
||||||
|
|
||||||
|
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.
|
||||||
|
mesh.subMeshCount = submeshCount;
|
||||||
|
for (int i = 0; i < submeshCount; i++)
|
||||||
|
mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 {
|
||||||
|
public readonly Mesh mesh = SpineMesh.NewMesh();
|
||||||
|
readonly ExposedList<Attachment> attachmentsUsed = new ExposedList<Attachment>();
|
||||||
|
readonly ExposedList<SubmeshInstruction> instructionsUsed = new ExposedList<SubmeshInstruction>();
|
||||||
|
|
||||||
|
public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, SubmeshedMeshInstruction instruction) {
|
||||||
|
mesh.vertices = verts;
|
||||||
|
mesh.uv = uvs;
|
||||||
|
mesh.colors32 = colors;
|
||||||
|
|
||||||
|
attachmentsUsed.Clear(false);
|
||||||
|
attachmentsUsed.GrowIfNeeded(instruction.attachmentList.Capacity);
|
||||||
|
attachmentsUsed.Count = instruction.attachmentList.Count;
|
||||||
|
instruction.attachmentList.CopyTo(attachmentsUsed.Items);
|
||||||
|
|
||||||
|
instructionsUsed.Clear(false);
|
||||||
|
instructionsUsed.GrowIfNeeded(instruction.submeshInstructions.Capacity);
|
||||||
|
instructionsUsed.Count = instruction.submeshInstructions.Count;
|
||||||
|
instruction.submeshInstructions.CopyTo(instructionsUsed.Items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StructureDoesntMatch (SubmeshedMeshInstruction instructions) {
|
||||||
|
// Check count inequality.
|
||||||
|
if (instructions.attachmentList.Count != this.attachmentsUsed.Count) return true;
|
||||||
|
if (instructions.submeshInstructions.Count != this.instructionsUsed.Count) return true;
|
||||||
|
|
||||||
|
// Check each attachment.
|
||||||
|
var attachmentsPassed = instructions.attachmentList.Items;
|
||||||
|
var myAttachments = this.attachmentsUsed.Items;
|
||||||
|
for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
|
||||||
|
if (attachmentsPassed[i] != myAttachments[i]) return true;
|
||||||
|
|
||||||
|
// Check each submesh for equal arrangement.
|
||||||
|
var instructionListItems = instructions.submeshInstructions.Items;
|
||||||
|
var myInstructions = this.instructionsUsed.Items;
|
||||||
|
for (int i = 0, n = this.instructionsUsed.Count; i < n; i++) {
|
||||||
|
var lhs = instructionListItems[i];
|
||||||
|
var rhs = myInstructions[i];
|
||||||
|
if (
|
||||||
|
lhs.material.GetInstanceID() != rhs.material.GetInstanceID() ||
|
||||||
|
lhs.startSlot != rhs.startSlot ||
|
||||||
|
lhs.endSlot != rhs.endSlot ||
|
||||||
|
lhs.triangleCount != rhs.triangleCount ||
|
||||||
|
lhs.vertexCount != rhs.vertexCount ||
|
||||||
|
lhs.firstVertexIndex != rhs.firstVertexIndex
|
||||||
|
) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Debug.Log("structure matched");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
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,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 15418e02758268b488e9ab86f805330c
|
guid: 4b4bb4dc3775d6548b9405f681601abb
|
||||||
timeCreated: 1457407060
|
timeCreated: 1458056219
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@ -1,315 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace Spine.Unity {
|
|
||||||
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;
|
|
||||||
Color32 color;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int vertexIndex = 0;
|
|
||||||
var skeleton = instruction.skeleton;
|
|
||||||
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
|
|
||||||
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
|
|
||||||
|
|
||||||
// Push verts in this submesh
|
|
||||||
for (int slotIndex = instruction.startSlot, endSlot = instruction.endSlot; slotIndex < endSlot; slotIndex++) {
|
|
||||||
var slot = skeletonDrawOrderItems[slotIndex];
|
|
||||||
var attachment = slot.attachment;
|
|
||||||
float z = slotIndex * zSpacing;
|
|
||||||
|
|
||||||
var regionAttachment = attachment as RegionAttachment;
|
|
||||||
if (regionAttachment != null) {
|
|
||||||
attachmentList.Add(attachment);
|
|
||||||
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;
|
|
||||||
|
|
||||||
color.a = (byte)(a * slot.a * regionAttachment.a);
|
|
||||||
if (this.premultiplyVertexColors) {
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
color.r = (byte)(r * slot.r * regionAttachment.r); color.g = (byte)(g * slot.g * regionAttachment.g); color.b = (byte)(b * slot.b * regionAttachment.b);
|
|
||||||
}
|
|
||||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
|
||||||
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) {
|
|
||||||
attachmentList.Add(attachment);
|
|
||||||
int meshVertexCount = meshAttachment.vertices.Length;
|
|
||||||
if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
|
|
||||||
meshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
|
||||||
|
|
||||||
color.a = (byte)(a * slot.a * meshAttachment.a);
|
|
||||||
if (this.premultiplyVertexColors) {
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
color.r = (byte)(r * slot.r * meshAttachment.r);
|
|
||||||
color.g = (byte)(g * slot.g * meshAttachment.g);
|
|
||||||
color.b = (byte)(b * slot.b * meshAttachment.b);
|
|
||||||
}
|
|
||||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
attachmentList.Add(attachment);
|
|
||||||
int meshVertexCount = weightedMeshAttachment.uvs.Length;
|
|
||||||
if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
|
|
||||||
weightedMeshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
|
||||||
|
|
||||||
color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
|
|
||||||
if (this.premultiplyVertexColors) {
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
color.r = (byte)(r * slot.r * weightedMeshAttachment.r);
|
|
||||||
color.g = (byte)(g * slot.g * weightedMeshAttachment.g);
|
|
||||||
color.b = (byte)(b * slot.b * weightedMeshAttachment.b);
|
|
||||||
}
|
|
||||||
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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,494 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Spine;
|
|
||||||
using Spine.Unity;
|
|
||||||
|
|
||||||
public class ArraysSubmeshedMeshGenerator : Spine.Unity.ISubmeshedMeshGenerator {
|
|
||||||
|
|
||||||
readonly List<Slot> separators = new List<Slot>();
|
|
||||||
public List<Slot> Separators { get { return this.separators; } }
|
|
||||||
|
|
||||||
public bool generateNormals;
|
|
||||||
public bool generateTangents;
|
|
||||||
|
|
||||||
public float zSpacing = 0f;
|
|
||||||
|
|
||||||
public SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton) {
|
|
||||||
if (skeleton == null) throw new System.ArgumentNullException("skeleton");
|
|
||||||
|
|
||||||
// Count vertices and submesh triangles.
|
|
||||||
int runningVertexCount = 0;
|
|
||||||
|
|
||||||
int submeshTriangleCount = 0;
|
|
||||||
int submeshFirstVertex = 0;
|
|
||||||
int submeshVertexCount = 0;
|
|
||||||
int submeshStartSlotIndex = 0;
|
|
||||||
Material lastMaterial = null;
|
|
||||||
|
|
||||||
var drawOrder = skeleton.drawOrder;
|
|
||||||
var drawOrderItems = drawOrder.Items;
|
|
||||||
int drawOrderCount = drawOrder.Count;
|
|
||||||
int separatorCount = separators.Count;
|
|
||||||
|
|
||||||
var instructionList = this.currentInstructions.submeshInstructions;
|
|
||||||
instructionList.Clear(false);
|
|
||||||
|
|
||||||
currentInstructions.attachmentList.Clear(false);
|
|
||||||
|
|
||||||
for (int i = 0; i < drawOrderCount; i++) {
|
|
||||||
var slot = drawOrderItems[i];
|
|
||||||
var attachment = slot.attachment;
|
|
||||||
|
|
||||||
object rendererObject; // An AtlasRegion in plain Spine-Unity. eventual source of Material object.
|
|
||||||
int attachmentVertexCount, attachmentTriangleCount;
|
|
||||||
|
|
||||||
var regionAttachment = attachment as RegionAttachment;
|
|
||||||
if (regionAttachment != null) {
|
|
||||||
rendererObject = regionAttachment.RendererObject;
|
|
||||||
attachmentVertexCount = 4;
|
|
||||||
attachmentTriangleCount = 6;
|
|
||||||
} else {
|
|
||||||
var meshAttachment = attachment as MeshAttachment;
|
|
||||||
if (meshAttachment != null) {
|
|
||||||
rendererObject = meshAttachment.RendererObject;
|
|
||||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1;
|
|
||||||
attachmentTriangleCount = meshAttachment.triangles.Length;
|
|
||||||
} else {
|
|
||||||
var skinnedMeshAttachment = attachment as WeightedMeshAttachment;
|
|
||||||
if (skinnedMeshAttachment != null) {
|
|
||||||
rendererObject = skinnedMeshAttachment.RendererObject;
|
|
||||||
attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
|
|
||||||
attachmentTriangleCount = skinnedMeshAttachment.triangles.Length;
|
|
||||||
} else
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) )) {
|
|
||||||
|
|
||||||
instructionList.Add(
|
|
||||||
new SubmeshInstruction {
|
|
||||||
skeleton = skeleton,
|
|
||||||
material = lastMaterial,
|
|
||||||
triangleCount = submeshTriangleCount,
|
|
||||||
vertexCount = submeshVertexCount,
|
|
||||||
startSlot = submeshStartSlotIndex,
|
|
||||||
endSlot = i,
|
|
||||||
firstVertexIndex = submeshFirstVertex
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Prepare for next submesh
|
|
||||||
submeshTriangleCount = 0;
|
|
||||||
submeshVertexCount = 0;
|
|
||||||
submeshFirstVertex = runningVertexCount;
|
|
||||||
submeshStartSlotIndex = i;
|
|
||||||
}
|
|
||||||
lastMaterial = attachmentMaterial;
|
|
||||||
|
|
||||||
submeshTriangleCount += attachmentTriangleCount;
|
|
||||||
submeshVertexCount += attachmentVertexCount;
|
|
||||||
runningVertexCount += attachmentVertexCount;
|
|
||||||
|
|
||||||
currentInstructions.attachmentList.Add(attachment);
|
|
||||||
}
|
|
||||||
|
|
||||||
instructionList.Add(
|
|
||||||
new SubmeshInstruction {
|
|
||||||
skeleton = skeleton,
|
|
||||||
material = lastMaterial,
|
|
||||||
triangleCount = submeshTriangleCount,
|
|
||||||
vertexCount = submeshVertexCount,
|
|
||||||
startSlot = submeshStartSlotIndex,
|
|
||||||
endSlot = drawOrderCount,
|
|
||||||
firstVertexIndex = submeshFirstVertex
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
currentInstructions.vertexCount = runningVertexCount;
|
|
||||||
return currentInstructions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubmeshedMesh GenerateMesh (SubmeshedMeshInstruction meshInstructions) {
|
|
||||||
var smartMesh = doubleBufferedSmartMesh.GetNextMesh();
|
|
||||||
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;
|
|
||||||
Color32 color;
|
|
||||||
|
|
||||||
// Ensure correct buffer sizes.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 meshBoundsMin;
|
|
||||||
Vector3 meshBoundsMax;
|
|
||||||
|
|
||||||
int attachmentCount = meshInstructions.attachmentList.Count;
|
|
||||||
|
|
||||||
// Initial values for manual Mesh Bounds calculation
|
|
||||||
if (meshInstructions.attachmentList.Count <= 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
|
|
||||||
var currentSubmeshInstruction = instructionList.Items[submeshIndex];
|
|
||||||
var skeleton = currentSubmeshInstruction.skeleton;
|
|
||||||
var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
|
|
||||||
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
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) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
|
|
||||||
meshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount];
|
|
||||||
weightedMeshAttachment.ComputeWorldVertices(slot, attVertBuffer);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin);
|
|
||||||
Vector3 meshCenter = meshBoundsMin + meshBoundsExtents * 0.5f;
|
|
||||||
|
|
||||||
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, meshInstructions);
|
|
||||||
mesh.bounds = new Bounds(meshCenter, meshBoundsExtents);
|
|
||||||
|
|
||||||
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.
|
|
||||||
mesh.subMeshCount = submeshCount;
|
|
||||||
for (int i = 0; i < submeshCount; i++)
|
|
||||||
mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return new SubmeshedMesh(smartMesh.mesh, sharedMaterials);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Internals
|
|
||||||
readonly DoubleBufferedSmartMesh doubleBufferedSmartMesh = new DoubleBufferedSmartMesh();
|
|
||||||
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
|
|
||||||
class SubmeshTriangleBuffer {
|
|
||||||
public int[] triangles;
|
|
||||||
public int triangleCount;
|
|
||||||
|
|
||||||
public SubmeshTriangleBuffer (int triangleCount) {
|
|
||||||
triangles = new int[triangleCount];
|
|
||||||
this.triangleCount = triangleCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DoubleBufferedSmartMesh {
|
|
||||||
readonly SmartMesh mesh1 = new SmartMesh();
|
|
||||||
readonly SmartMesh mesh2 = new SmartMesh();
|
|
||||||
bool usingMesh1;
|
|
||||||
|
|
||||||
public SmartMesh GetNextMesh () {
|
|
||||||
usingMesh1 = !usingMesh1;
|
|
||||||
return usingMesh1 ? mesh1 : mesh2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it.
|
|
||||||
class SmartMesh {
|
|
||||||
public readonly Mesh mesh = SpineMesh.NewMesh();
|
|
||||||
readonly ExposedList<Attachment> attachmentsUsed = new ExposedList<Attachment>();
|
|
||||||
readonly ExposedList<SubmeshInstruction> instructionsUsed = new ExposedList<SubmeshInstruction>();
|
|
||||||
|
|
||||||
public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, SubmeshedMeshInstruction instruction) {
|
|
||||||
mesh.vertices = verts;
|
|
||||||
mesh.uv = uvs;
|
|
||||||
mesh.colors32 = colors;
|
|
||||||
|
|
||||||
attachmentsUsed.Clear(false);
|
|
||||||
attachmentsUsed.GrowIfNeeded(instruction.attachmentList.Capacity);
|
|
||||||
attachmentsUsed.Count = instruction.attachmentList.Count;
|
|
||||||
instruction.attachmentList.CopyTo(attachmentsUsed.Items);
|
|
||||||
|
|
||||||
instructionsUsed.Clear(false);
|
|
||||||
instructionsUsed.GrowIfNeeded(instruction.submeshInstructions.Capacity);
|
|
||||||
instructionsUsed.Count = instruction.submeshInstructions.Count;
|
|
||||||
instruction.submeshInstructions.CopyTo(instructionsUsed.Items);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool StructureDoesntMatch (SubmeshedMeshInstruction instructions) {
|
|
||||||
// Check count inequality.
|
|
||||||
if (instructions.attachmentList.Count != this.attachmentsUsed.Count) return true;
|
|
||||||
if (instructions.submeshInstructions.Count != this.instructionsUsed.Count) return true;
|
|
||||||
|
|
||||||
// Check each attachment.
|
|
||||||
var attachmentsPassed = instructions.attachmentList.Items;
|
|
||||||
var myAttachments = this.attachmentsUsed.Items;
|
|
||||||
for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
|
|
||||||
if (attachmentsPassed[i] != myAttachments[i]) return true;
|
|
||||||
|
|
||||||
// Check each submesh for equal arrangement.
|
|
||||||
var instructionListItems = instructions.submeshInstructions.Items;
|
|
||||||
var myInstructions = this.instructionsUsed.Items;
|
|
||||||
for (int i = 0, n = this.instructionsUsed.Count; i < n; i++) {
|
|
||||||
var lhs = instructionListItems[i];
|
|
||||||
var rhs = myInstructions[i];
|
|
||||||
if (
|
|
||||||
lhs.material.GetInstanceID() != rhs.material.GetInstanceID() ||
|
|
||||||
lhs.startSlot != rhs.startSlot ||
|
|
||||||
lhs.endSlot != rhs.endSlot ||
|
|
||||||
lhs.triangleCount != rhs.triangleCount ||
|
|
||||||
lhs.vertexCount != rhs.vertexCount ||
|
|
||||||
lhs.firstVertexIndex != rhs.firstVertexIndex
|
|
||||||
) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Debug.Log("structure matched");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@ -1,33 +1,35 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Spine.Unity {
|
namespace Spine.Unity.MeshGeneration {
|
||||||
|
// ISubmeshedMeshGenerator:
|
||||||
|
// How to use:
|
||||||
|
// Step 1: Have a SubmeshedMeshGenerator instance, and a Spine.Skeleton
|
||||||
|
// Step 2: Call GenerateInstruction. Pass it your Skeleton. Keep the return value (a SubmeshedMeshInstruction, you can use it in other classes too).
|
||||||
|
// Step 3: Pass the SubmeshedMeshInstruction into GenerateMesh. You'll get a Mesh and Materials.
|
||||||
|
// Step 4: Put the Mesh in MeshFilter. Put the Materials in MeshRenderer.sharedMaterials.
|
||||||
public interface ISubmeshedMeshGenerator {
|
public interface ISubmeshedMeshGenerator {
|
||||||
/// <summary>Generates instructions for how to generate the submeshed mesh based on the given state of the
|
|
||||||
/// skeleton. The returned instructions can be used to generate a whole submeshed mesh or individual submeshes.</summary>
|
|
||||||
SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton);
|
SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton);
|
||||||
|
MeshAndMaterials GenerateMesh (SubmeshedMeshInstruction wholeMeshInstruction);
|
||||||
/// <summary>Returns a SubmeshedMesh (a mesh and a material array coupled in a struct).
|
|
||||||
/// Call GenerateInstructions to get the SubmeshedMeshInstructions to pass into this.</summary>
|
|
||||||
SubmeshedMesh GenerateMesh (SubmeshedMeshInstruction wholeMeshInstruction);
|
|
||||||
|
|
||||||
/// <summary>A list of slots that mark the end of a submesh. The slot after it will be the start of a new submesh.</summary>
|
|
||||||
List<Slot> Separators { get; }
|
List<Slot> Separators { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ISingleSubmeshGenerator {
|
// ISubmeshSetMeshGenerator
|
||||||
Mesh GenerateMesh (SubmeshInstruction instruction);
|
// How to use:
|
||||||
|
// Step 1: Get a list of SubmeshInstruction. You can get this from SkeletonRenderer or an ISubmeshedMeshGenerator's returned SubmeshedMeshInstruction.
|
||||||
|
// Step 2: Call AddInstruction one by one, or AddInstructions once.
|
||||||
|
// Step 3: Call GenerateMesh. You'll get a Mesh and Materials.
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>A Submeshed mesh is a return type so the mesh with
|
// ISingleSubmeshGenerator
|
||||||
/// multiple submeshes can be coupled with a material array to render its submeshes.</summary>
|
// How to use:
|
||||||
public struct SubmeshedMesh {
|
// Step 1: Have a single SubmeshInstruction
|
||||||
public readonly Mesh mesh;
|
// Step 2:
|
||||||
public readonly Material[] materials;
|
public interface ISingleSubmeshGenerator {
|
||||||
public SubmeshedMesh (Mesh mesh, Material[] materials) {
|
Mesh GenerateMesh (SubmeshInstruction instruction);
|
||||||
this.mesh = mesh;
|
|
||||||
this.materials = materials;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes.</summary>
|
/// <summary>Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes.</summary>
|
||||||
@ -65,5 +67,33 @@ namespace Spine.Unity {
|
|||||||
|
|
||||||
// Vertex index offset. Used by submesh generation if part of a bigger mesh.
|
// Vertex index offset. Used by submesh generation if part of a bigger mesh.
|
||||||
public int firstVertexIndex;
|
public int firstVertexIndex;
|
||||||
|
public bool separatedBySlot;
|
||||||
|
|
||||||
|
/// <summary>The number of slots in this SubmeshInstruction's range. Not necessarily the number of attachments.</summary>
|
||||||
|
public int SlotCount { get { return endSlot - startSlot; } }
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Material[] GetNewMaterialArray (this ExposedList<SubmeshInstruction> instructions) {
|
||||||
|
var materials = new Material[instructions.Count];
|
||||||
|
instructions.FillMaterialArray(materials);
|
||||||
|
return materials;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MeshAndMaterials {
|
||||||
|
public readonly Mesh mesh;
|
||||||
|
public readonly Material[] materials;
|
||||||
|
|
||||||
|
public MeshAndMaterials (Mesh mesh, Material[] materials) {
|
||||||
|
this.mesh = mesh;
|
||||||
|
this.materials = materials;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: c518552ea622e98418ce673c5febc468
|
guid: 7492a8ff24ea1884f92307ab1121e451
|
||||||
timeCreated: 1455406760
|
timeCreated: 1458046464
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
|
|||||||
@ -3,14 +3,12 @@ using System.Collections;
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using Spine.Unity;
|
using Spine.Unity;
|
||||||
|
|
||||||
[CustomEditor(typeof(SubmeshRenderer))]
|
[CustomEditor(typeof(SkeletonRenderPart))]
|
||||||
public class SubmeshRendererInspector : Editor {
|
public class SkeletonRenderPartInspector : Editor {
|
||||||
|
|
||||||
SpineInspectorUtility.SerializedSortingProperties sortingProperties;
|
SpineInspectorUtility.SerializedSortingProperties sortingProperties;
|
||||||
SubmeshRenderer component;
|
|
||||||
|
|
||||||
void OnEnable () {
|
void OnEnable () {
|
||||||
component = target as SubmeshRenderer;
|
var component = target as Component;
|
||||||
sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(component.GetComponent<MeshRenderer>());
|
sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(component.GetComponent<MeshRenderer>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,5 +16,4 @@ public class SubmeshRendererInspector : Editor {
|
|||||||
DrawDefaultInspector();
|
DrawDefaultInspector();
|
||||||
SpineInspectorUtility.SortingPropertyFields(sortingProperties, true);
|
SpineInspectorUtility.SortingPropertyFields(sortingProperties, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 30e43037bf4433645ad70266f34c1c8b
|
||||||
|
timeCreated: 1458051036
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
using UnityEditor;
|
||||||
|
using Spine.Unity;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(SkeletonRenderSeparator))]
|
||||||
|
public class SkeletonRenderSeparatorInspector : Editor {
|
||||||
|
|
||||||
|
SkeletonRenderSeparator component;
|
||||||
|
|
||||||
|
void OnEnable () {
|
||||||
|
component = target as SkeletonRenderSeparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI () {
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Add Renderer")) {
|
||||||
|
const int SortingOrderIncrement = 5;
|
||||||
|
int index = component.renderers.Count;
|
||||||
|
var smr = SkeletonRenderPart.NewSubmeshRendererGameObject(component.transform, index.ToString());
|
||||||
|
component.renderers.Add(smr);
|
||||||
|
|
||||||
|
// increment renderer sorting order.
|
||||||
|
if (index != 0) {
|
||||||
|
var prev = component.renderers[index - 1];
|
||||||
|
if (prev != null) {
|
||||||
|
var prevMeshRenderer = prev.GetComponent<MeshRenderer>();
|
||||||
|
var currentMeshRenderer = smr.GetComponent<MeshRenderer>();
|
||||||
|
if (prevMeshRenderer != null && currentMeshRenderer != null) {
|
||||||
|
int prevSortingLayer = prevMeshRenderer.sortingLayerID;
|
||||||
|
int prevSortingOrder = prevMeshRenderer.sortingOrder;
|
||||||
|
|
||||||
|
currentMeshRenderer.sortingLayerID = prevSortingLayer;
|
||||||
|
currentMeshRenderer.sortingOrder = prevSortingOrder + SortingOrderIncrement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Destroy Renderers")) {
|
||||||
|
if (EditorUtility.DisplayDialog("Destroy Submesh Renderers", "Do you really want to destroy all the SubmeshRenderer GameObjects in the list?", "Destroy", "Cancel")) {
|
||||||
|
|
||||||
|
for (int i = 0; i < component.renderers.Count; i++) {
|
||||||
|
Debug.LogFormat("Destroying {0}", component.renderers[i].gameObject.name);
|
||||||
|
DestroyImmediate(component.renderers[i].gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
component.renderers.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d2a5062cfe5dd4344831cda4723128af
|
||||||
|
timeCreated: 1458067064
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -1,13 +1,12 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using Spine.Unity.MeshGeneration;
|
||||||
using UnityEngine.Assertions;
|
|
||||||
|
|
||||||
namespace Spine.Unity {
|
namespace Spine.Unity {
|
||||||
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
|
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
|
||||||
public class SubmeshRenderer : MonoBehaviour {
|
public class SkeletonRenderPart : MonoBehaviour {
|
||||||
ISingleSubmeshGenerator meshGenerator;
|
ISubmeshSetMeshGenerator meshGenerator;
|
||||||
public ISingleSubmeshGenerator MeshGenerator {
|
public ISubmeshSetMeshGenerator MeshGenerator {
|
||||||
get {
|
get {
|
||||||
LazyIntialize();
|
LazyIntialize();
|
||||||
return meshGenerator;
|
return meshGenerator;
|
||||||
@ -32,7 +31,7 @@ namespace Spine.Unity {
|
|||||||
|
|
||||||
void LazyIntialize () {
|
void LazyIntialize () {
|
||||||
if (meshGenerator != null) return;
|
if (meshGenerator != null) return;
|
||||||
meshGenerator = new ArraysSingleSubmeshGenerator(); // swap this out with your custom ISingleSubmeshGenerator code.
|
meshGenerator = new ArraysSubmeshSetMeshGenerator();
|
||||||
meshFilter = GetComponent<MeshFilter>();
|
meshFilter = GetComponent<MeshFilter>();
|
||||||
meshRenderer = GetComponent<MeshRenderer>();
|
meshRenderer = GetComponent<MeshRenderer>();
|
||||||
}
|
}
|
||||||
@ -41,10 +40,11 @@ namespace Spine.Unity {
|
|||||||
meshFilter.sharedMesh = null;
|
meshFilter.sharedMesh = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenderSubmesh (SubmeshInstruction instruction) {
|
public void RenderSubmesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
|
||||||
LazyIntialize();
|
LazyIntialize();
|
||||||
meshRenderer.sharedMaterial = instruction.material;
|
MeshAndMaterials m = meshGenerator.GenerateMesh(instructions, startSubmesh, endSubmesh);
|
||||||
meshFilter.sharedMesh = meshGenerator.GenerateMesh(instruction);
|
meshFilter.sharedMesh = m.mesh;
|
||||||
|
meshRenderer.sharedMaterials = m.materials;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPropertyBlock (MaterialPropertyBlock block) {
|
public void SetPropertyBlock (MaterialPropertyBlock block) {
|
||||||
@ -52,10 +52,10 @@ namespace Spine.Unity {
|
|||||||
meshRenderer.SetPropertyBlock(block);
|
meshRenderer.SetPropertyBlock(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SubmeshRenderer NewSubmeshRendererGameObject (Transform parent, string name) {
|
public static SkeletonRenderPart NewSubmeshRendererGameObject (Transform parent, string name) {
|
||||||
var go = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
|
var go = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
|
||||||
go.transform.SetParent(parent, false);
|
go.transform.SetParent(parent, false);
|
||||||
var returnComponent = go.AddComponent<SubmeshRenderer>();
|
var returnComponent = go.AddComponent<SkeletonRenderPart>();
|
||||||
|
|
||||||
return returnComponent;
|
return returnComponent;
|
||||||
}
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1c0b968d1e7333b499e347acb644f1c1
|
||||||
|
timeCreated: 1458045480
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Spine.Unity;
|
||||||
|
|
||||||
|
namespace Spine.Unity {
|
||||||
|
[ExecuteInEditMode]
|
||||||
|
public class SkeletonRenderSeparator : MonoBehaviour {
|
||||||
|
[SerializeField]
|
||||||
|
protected SkeletonRenderer skeletonRenderer;
|
||||||
|
public SkeletonRenderer SkeletonRenderer {
|
||||||
|
get { return skeletonRenderer; }
|
||||||
|
set {
|
||||||
|
if (skeletonRenderer != null)
|
||||||
|
skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender;
|
||||||
|
skeletonRenderer = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MeshRenderer masterMeshRenderer;
|
||||||
|
|
||||||
|
[Header("Settings")]
|
||||||
|
public bool propagateMaterialPropertyBlock = false;
|
||||||
|
public bool controlMainMeshRenderer = true;
|
||||||
|
|
||||||
|
[Space(10f)]
|
||||||
|
public List<Spine.Unity.SkeletonRenderPart> renderers = new List<SkeletonRenderPart>();
|
||||||
|
|
||||||
|
void Reset () {
|
||||||
|
if (skeletonRenderer == null) {
|
||||||
|
skeletonRenderer = GetComponent<SkeletonRenderer>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnEnable () {
|
||||||
|
if (skeletonRenderer == null) return;
|
||||||
|
if (block == null) block = new MaterialPropertyBlock();
|
||||||
|
masterMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
|
||||||
|
|
||||||
|
if (controlMainMeshRenderer)
|
||||||
|
masterMeshRenderer.enabled = false;
|
||||||
|
|
||||||
|
skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender;
|
||||||
|
skeletonRenderer.GenerateMeshOverride += SeparateSkeletonRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDisable () {
|
||||||
|
if (skeletonRenderer == null) return;
|
||||||
|
|
||||||
|
if (controlMainMeshRenderer)
|
||||||
|
masterMeshRenderer.enabled = true;
|
||||||
|
|
||||||
|
skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender;
|
||||||
|
|
||||||
|
foreach (var s in renderers)
|
||||||
|
s.ClearMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialPropertyBlock block;
|
||||||
|
|
||||||
|
void SeparateSkeletonRender (SkeletonRenderer.SmartMesh.Instruction instruction) {
|
||||||
|
int rendererCount = renderers.Count;
|
||||||
|
if (rendererCount <= 0) return;
|
||||||
|
|
||||||
|
int rendererIndex = 0;
|
||||||
|
|
||||||
|
if (propagateMaterialPropertyBlock)
|
||||||
|
masterMeshRenderer.GetPropertyBlock(block);
|
||||||
|
|
||||||
|
var submeshInstructions = instruction.submeshInstructions;
|
||||||
|
var submeshInstructionsItems = submeshInstructions.Items;
|
||||||
|
int lastSubmeshInstruction = submeshInstructions.Count - 1;
|
||||||
|
|
||||||
|
var currentRenderer = renderers[rendererIndex];
|
||||||
|
for (int i = 0, start = 0; i <= lastSubmeshInstruction; i++) {
|
||||||
|
if (submeshInstructionsItems[i].separatedBySlot) {
|
||||||
|
//Debug.Log(submeshInstructionsItems[i].endSlot);
|
||||||
|
currentRenderer.RenderSubmesh(instruction.submeshInstructions, start, i + 1);
|
||||||
|
start = i + 1;
|
||||||
|
|
||||||
|
if (propagateMaterialPropertyBlock)
|
||||||
|
currentRenderer.SetPropertyBlock(block);
|
||||||
|
|
||||||
|
rendererIndex++;
|
||||||
|
if (rendererIndex < rendererCount) {
|
||||||
|
currentRenderer = renderers[rendererIndex];
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (i == lastSubmeshInstruction) {
|
||||||
|
//Debug.Log(submeshInstructionsItems[i].endSlot);
|
||||||
|
currentRenderer.RenderSubmesh(instruction.submeshInstructions, start, i + 1);
|
||||||
|
|
||||||
|
if (propagateMaterialPropertyBlock)
|
||||||
|
currentRenderer.SetPropertyBlock(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,36 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
using System.Collections;
|
|
||||||
using UnityEditor;
|
|
||||||
using Spine.Unity;
|
|
||||||
|
|
||||||
[CustomEditor(typeof(SpineRenderSeparator))]
|
|
||||||
public class SpineRenderSeparatorInspector : Editor {
|
|
||||||
|
|
||||||
SpineRenderSeparator component;
|
|
||||||
|
|
||||||
void OnEnable () {
|
|
||||||
component = target as SpineRenderSeparator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnInspectorGUI () {
|
|
||||||
base.OnInspectorGUI();
|
|
||||||
if (GUILayout.Button("Destroy Submesh Renderers")) {
|
|
||||||
if (EditorUtility.DisplayDialog("Destroy Submesh Renderers", "Do you really want to destroy all the SubmeshRenderer GameObjects in the list?", "Destroy", "Cancel")) {
|
|
||||||
|
|
||||||
for (int i = 0; i < component.submeshRenderers.Count; i++) {
|
|
||||||
Debug.LogFormat("Destroying {0}", component.submeshRenderers[i].gameObject.name);
|
|
||||||
DestroyImmediate(component.submeshRenderers[i].gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
component.submeshRenderers.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GUILayout.Button("Add SubmeshRenderer")) {
|
|
||||||
int index = component.submeshRenderers.Count;
|
|
||||||
var smr = SubmeshRenderer.NewSubmeshRendererGameObject(component.transform, index.ToString());
|
|
||||||
component.submeshRenderers.Add(smr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Spine.Unity;
|
|
||||||
|
|
||||||
[ExecuteInEditMode]
|
|
||||||
public class SpineRenderSeparator : MonoBehaviour {
|
|
||||||
[SerializeField]
|
|
||||||
protected SkeletonRenderer skeletonRenderer;
|
|
||||||
public SkeletonRenderer SkeletonRenderer {
|
|
||||||
get { return skeletonRenderer; }
|
|
||||||
set {
|
|
||||||
if (skeletonRenderer != null)
|
|
||||||
skeletonRenderer.GenerateMeshOverride -= SeparatelyRenderSubmeshes;
|
|
||||||
skeletonRenderer = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MeshRenderer masterMeshRenderer;
|
|
||||||
|
|
||||||
public List<Spine.Unity.SubmeshRenderer> submeshRenderers = new List<SubmeshRenderer>();
|
|
||||||
public bool propagateMaterialPropertyBlock = false;
|
|
||||||
|
|
||||||
void OnEnable () {
|
|
||||||
if (skeletonRenderer == null) return;
|
|
||||||
if (block == null) block = new MaterialPropertyBlock();
|
|
||||||
masterMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
|
|
||||||
masterMeshRenderer.enabled = false;
|
|
||||||
skeletonRenderer.GenerateMeshOverride -= SeparatelyRenderSubmeshes;
|
|
||||||
skeletonRenderer.GenerateMeshOverride += SeparatelyRenderSubmeshes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDisable () {
|
|
||||||
if (skeletonRenderer == null) return;
|
|
||||||
|
|
||||||
masterMeshRenderer.enabled = true;
|
|
||||||
skeletonRenderer.GenerateMeshOverride -= SeparatelyRenderSubmeshes;
|
|
||||||
|
|
||||||
foreach (var s in submeshRenderers)
|
|
||||||
s.ClearMesh();
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialPropertyBlock block;
|
|
||||||
|
|
||||||
void SeparatelyRenderSubmeshes (SkeletonRenderer.SmartMesh.Instruction instruction) {
|
|
||||||
var submeshInstructions = instruction.submeshInstructions;
|
|
||||||
var submeshInstructionsItems = submeshInstructions.Items;
|
|
||||||
for (int i = 0; i < instruction.submeshInstructions.Count; i++) {
|
|
||||||
if (i >= submeshRenderers.Count) return;
|
|
||||||
var submeshRenderer = submeshRenderers[i];
|
|
||||||
submeshRenderer.RenderSubmesh(submeshInstructionsItems[i]);
|
|
||||||
|
|
||||||
if (propagateMaterialPropertyBlock) {
|
|
||||||
masterMeshRenderer.GetPropertyBlock(block);
|
|
||||||
submeshRenderer.SetPropertyBlock(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 792af191c395561459cc0cc03a517ae8
|
|
||||||
folderAsset: yes
|
|
||||||
timeCreated: 1457401769
|
|
||||||
licenseType: Free
|
|
||||||
DefaultImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5eec1664658bb4344909619707652674
|
|
||||||
folderAsset: yes
|
|
||||||
timeCreated: 1457405608
|
|
||||||
licenseType: Free
|
|
||||||
DefaultImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -33,6 +33,7 @@ using System.Collections.Generic;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Spine;
|
using Spine;
|
||||||
using Spine.Unity;
|
using Spine.Unity;
|
||||||
|
using Spine.Unity.MeshGeneration;
|
||||||
|
|
||||||
/// <summary>Renders a skeleton.</summary>
|
/// <summary>Renders a skeleton.</summary>
|
||||||
[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent]
|
[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent]
|
||||||
@ -255,18 +256,18 @@ public class SkeletonRenderer : MonoBehaviour {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator)
|
// Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator)
|
||||||
if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) ||
|
bool separatedBySlot = (submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot));
|
||||||
(submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) {
|
if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) || separatedBySlot) {
|
||||||
|
|
||||||
workingSubmeshInstructions.Add(
|
workingSubmeshInstructions.Add(
|
||||||
new Spine.Unity.SubmeshInstruction {
|
new Spine.Unity.MeshGeneration.SubmeshInstruction {
|
||||||
skeleton = this.skeleton,
|
skeleton = this.skeleton,
|
||||||
material = lastMaterial,
|
material = lastMaterial,
|
||||||
startSlot = submeshStartSlotIndex,
|
startSlot = submeshStartSlotIndex,
|
||||||
endSlot = i,
|
endSlot = i,
|
||||||
triangleCount = submeshTriangleCount,
|
triangleCount = submeshTriangleCount,
|
||||||
firstVertexIndex = submeshFirstVertex,
|
firstVertexIndex = submeshFirstVertex,
|
||||||
vertexCount = submeshVertexCount
|
vertexCount = submeshVertexCount,
|
||||||
|
separatedBySlot = separatedBySlot
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -283,14 +284,15 @@ public class SkeletonRenderer : MonoBehaviour {
|
|||||||
}
|
}
|
||||||
|
|
||||||
workingSubmeshInstructions.Add(
|
workingSubmeshInstructions.Add(
|
||||||
new Spine.Unity.SubmeshInstruction {
|
new Spine.Unity.MeshGeneration.SubmeshInstruction {
|
||||||
skeleton = this.skeleton,
|
skeleton = this.skeleton,
|
||||||
material = lastMaterial,
|
material = lastMaterial,
|
||||||
startSlot = submeshStartSlotIndex,
|
startSlot = submeshStartSlotIndex,
|
||||||
endSlot = drawOrderCount,
|
endSlot = drawOrderCount,
|
||||||
triangleCount = submeshTriangleCount,
|
triangleCount = submeshTriangleCount,
|
||||||
firstVertexIndex = submeshFirstVertex,
|
firstVertexIndex = submeshFirstVertex,
|
||||||
vertexCount = submeshVertexCount
|
vertexCount = submeshVertexCount,
|
||||||
|
separatedBySlot = false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -663,7 +665,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSubmesh (int submeshIndex, Spine.Unity.SubmeshInstruction submeshInstructions, ExposedList<bool> flipStates, bool isLastSubmesh) {
|
void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, ExposedList<bool> flipStates, bool isLastSubmesh) {
|
||||||
SubmeshTriangleBuffer currentSubmesh = submeshes.Items[submeshIndex];
|
SubmeshTriangleBuffer currentSubmesh = submeshes.Items[submeshIndex];
|
||||||
int[] triangles = currentSubmesh.triangles;
|
int[] triangles = currentSubmesh.triangles;
|
||||||
|
|
||||||
@ -799,7 +801,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
|||||||
public int vertexCount = -1;
|
public int vertexCount = -1;
|
||||||
public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
|
public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
|
||||||
public readonly ExposedList<bool> attachmentFlips = new ExposedList<bool>();
|
public readonly ExposedList<bool> attachmentFlips = new ExposedList<bool>();
|
||||||
public readonly ExposedList<Spine.Unity.SubmeshInstruction> submeshInstructions = new ExposedList<Spine.Unity.SubmeshInstruction>();
|
public readonly ExposedList<Spine.Unity.MeshGeneration.SubmeshInstruction> submeshInstructions = new ExposedList<Spine.Unity.MeshGeneration.SubmeshInstruction>();
|
||||||
|
|
||||||
public void Clear () {
|
public void Clear () {
|
||||||
this.attachments.Clear(false);
|
this.attachments.Clear(false);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user