-
-
Save pharan/44f1b72f2e418bbc1fdb to your computer and use it in GitHub Desktop.
December 2015 Update of SkeletonRenderer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
* Spine Runtimes Software License | |
* Version 2.3 | |
* | |
* Copyright (c) 2013-2015, Esoteric Software | |
* All rights reserved. | |
* | |
* You are granted a perpetual, non-exclusive, non-sublicensable and | |
* non-transferable license to use, install, execute and perform the Spine | |
* Runtimes Software (the "Software") and derivative works solely for personal | |
* or internal use. Without the written permission of Esoteric Software (see | |
* Section 2 of the Spine Software License Agreement), you may not (a) modify, | |
* translate, adapt or otherwise create derivative works, improvements of the | |
* Software or develop new applications using the Software or (b) remove, | |
* delete, alter or obscure any trademarks or any copyright, trademark, patent | |
* or other intellectual property or proprietary rights notices on or in the | |
* Software, including any copy thereof. Redistributions in binary or source | |
* form must include this license and terms. | |
* | |
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*****************************************************************************/ | |
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using Spine; | |
/// <summary>Renders a skeleton.</summary> | |
[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] | |
public class SkeletonRenderer : MonoBehaviour { | |
public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer); | |
public SkeletonRendererDelegate OnReset; | |
public SkeletonDataAsset skeletonDataAsset; | |
public String initialSkinName; | |
#region Advanced | |
public bool calculateNormals, calculateTangents; | |
public float zSpacing; | |
public bool renderMeshes = true, immutableTriangles; | |
public bool frontFacing; | |
public bool logErrors = false; | |
// Submesh Separation | |
[SpineSlot] public string[] submeshSeparators = new string[0]; | |
[HideInInspector] public List<Slot> submeshSeparatorSlots = new List<Slot>(); | |
#endregion | |
[System.NonSerialized] public bool valid; | |
[System.NonSerialized] public Skeleton skeleton; | |
private MeshRenderer meshRenderer; | |
private MeshFilter meshFilter; | |
private Mesh mesh1, mesh2; | |
private bool useMesh1; | |
private float[] tempVertices = new float[8]; | |
private Vector3[] vertices; | |
private Color32[] colors; | |
private Vector2[] uvs; | |
private Material[] sharedMaterials = new Material[0]; | |
private MeshState meshState = new MeshState(); | |
private readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>(); | |
private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>(); | |
private SkeletonUtilitySubmeshRenderer[] submeshRenderers; | |
public virtual void Awake () { | |
Reset(); | |
} | |
public virtual void Reset () { | |
if (meshFilter != null) | |
meshFilter.sharedMesh = null; | |
meshRenderer = GetComponent<MeshRenderer>(); | |
if (meshRenderer != null) meshRenderer.sharedMaterial = null; | |
if (mesh1 != null) { | |
if (Application.isPlaying) | |
Destroy(mesh1); | |
else | |
DestroyImmediate(mesh1); | |
} | |
if (mesh2 != null) { | |
if (Application.isPlaying) | |
Destroy(mesh2); | |
else | |
DestroyImmediate(mesh2); | |
} | |
meshState = new MeshState(); | |
mesh1 = null; | |
mesh2 = null; | |
vertices = null; | |
colors = null; | |
uvs = null; | |
sharedMaterials = new Material[0]; | |
submeshMaterials.Clear(); | |
submeshes.Clear(); | |
skeleton = null; | |
valid = false; | |
if (!skeletonDataAsset) { | |
if (logErrors) | |
Debug.LogError("Missing SkeletonData asset.", this); | |
return; | |
} | |
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false); | |
if (skeletonData == null) | |
return; | |
valid = true; | |
meshFilter = GetComponent<MeshFilter>(); | |
meshRenderer = GetComponent<MeshRenderer>(); | |
mesh1 = newMesh(); | |
mesh2 = newMesh(); | |
vertices = new Vector3[0]; | |
skeleton = new Skeleton(skeletonData); | |
if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default") | |
skeleton.SetSkin(initialSkinName); | |
submeshSeparatorSlots.Clear(); | |
for (int i = 0; i < submeshSeparators.Length; i++) { | |
submeshSeparatorSlots.Add(skeleton.FindSlot(submeshSeparators[i])); | |
} | |
CollectSubmeshRenderers(); | |
LateUpdate(); | |
if (OnReset != null) | |
OnReset(this); | |
} | |
public void CollectSubmeshRenderers () { | |
submeshRenderers = GetComponentsInChildren<SkeletonUtilitySubmeshRenderer>(); | |
} | |
public virtual void OnDestroy () { | |
if (mesh1 != null) { | |
if (Application.isPlaying) | |
Destroy(mesh1); | |
else | |
DestroyImmediate(mesh1); | |
} | |
if (mesh2 != null) { | |
if (Application.isPlaying) | |
Destroy(mesh2); | |
else | |
DestroyImmediate(mesh2); | |
} | |
mesh1 = null; | |
mesh2 = null; | |
} | |
private static Mesh newMesh () { | |
Mesh mesh = new Mesh(); | |
mesh.name = "Skeleton Mesh"; | |
mesh.hideFlags = HideFlags.HideAndDontSave; | |
mesh.MarkDynamic(); | |
return mesh; | |
} | |
public virtual void LateUpdate () { | |
if (!valid) | |
return; | |
// Exit early if there is nothing to render | |
if (!meshRenderer.enabled && submeshRenderers.Length == 0) | |
return; | |
// Count vertices and submesh triangles. | |
int vertexCount = 0; | |
int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; | |
Material lastMaterial = null; | |
ExposedList<Slot> drawOrder = skeleton.drawOrder; | |
int drawOrderCount = drawOrder.Count; | |
int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count; | |
bool renderMeshes = this.renderMeshes; | |
// Clear last state of attachments and submeshes | |
MeshState.SingleMeshState workingState = meshState.buffer; | |
var workingAttachments = workingState.attachments; | |
var workingFlips = workingState.attachmentsFlipState; | |
var workingSubmeshArguments = workingState.addSubmeshArguments; | |
workingAttachments.Clear(true); | |
workingState.UpdateAttachmentCount(drawOrderCount); | |
workingSubmeshArguments.Clear(false); | |
MeshState.SingleMeshState storedState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; | |
var storedAttachments = storedState.attachments; | |
var storedFlips = storedState.attachmentsFlipState; | |
bool mustUpdateMeshStructure = storedState.requiresUpdate || // Force update if the mesh was cleared. (prevents flickering due to incorrect state) | |
drawOrder.Count != storedAttachments.Count || // Number of slots changed (when does this happen?) | |
immutableTriangles != storedState.immutableTriangles; // Immutable Triangles flag changed. | |
for (int i = 0; i < drawOrderCount; i++) { | |
Slot slot = drawOrder.Items[i]; | |
Bone bone = slot.bone; | |
Attachment attachment = slot.attachment; | |
object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object. | |
int attachmentVertexCount, attachmentTriangleCount; | |
// Handle flipping for normals (for lighting). | |
bool worldScaleIsSameSigns = ((bone.worldScaleY >= 0f) == (bone.worldScaleX >= 0f)); | |
bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns); // TODO: bone flipX and flipY will be removed in Spine 3.0 | |
workingFlips.Items[i] = flip; | |
workingAttachments.Items[i] = attachment; | |
mustUpdateMeshStructure = mustUpdateMeshStructure || // Always prefer short circuited or. || and not |=. | |
(attachment != storedAttachments.Items[i]) || // Attachment order changed. // This relies on the drawOrder.Count != storedAttachments.Count check above as a bounds check. | |
(flip != storedFlips.Items[i]); // Flip states changed. | |
RegionAttachment regionAttachment = attachment as RegionAttachment; | |
if (regionAttachment != null) { | |
rendererObject = regionAttachment.RendererObject; | |
attachmentVertexCount = 4; | |
attachmentTriangleCount = 6; | |
} else { | |
if (!renderMeshes) | |
continue; | |
MeshAttachment meshAttachment = attachment as MeshAttachment; | |
if (meshAttachment != null) { | |
rendererObject = meshAttachment.RendererObject; | |
attachmentVertexCount = meshAttachment.vertices.Length >> 1; | |
attachmentTriangleCount = meshAttachment.triangles.Length; | |
} else { | |
SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; | |
if (skinnedMeshAttachment != null) { | |
rendererObject = skinnedMeshAttachment.RendererObject; | |
attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; | |
attachmentTriangleCount = skinnedMeshAttachment.triangles.Length; | |
} else | |
continue; | |
} | |
} | |
#if !SPINE_TK2D | |
Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; | |
#else | |
Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; | |
#endif | |
// Populate submesh when material changes. (or when forced to separate by a submeshSeparator) | |
if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) || | |
(submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) { | |
workingSubmeshArguments.Add( | |
new MeshState.AddSubmeshArguments { | |
material = lastMaterial, | |
startSlot = submeshStartSlotIndex, | |
endSlot = i, | |
triangleCount = submeshTriangleCount, | |
firstVertex = submeshFirstVertex, | |
isLastSubmesh = false | |
} | |
); | |
submeshTriangleCount = 0; | |
submeshFirstVertex = vertexCount; | |
submeshStartSlotIndex = i; | |
} | |
lastMaterial = material; | |
submeshTriangleCount += attachmentTriangleCount; | |
vertexCount += attachmentVertexCount; | |
} | |
workingSubmeshArguments.Add( | |
new MeshState.AddSubmeshArguments { | |
material = lastMaterial, | |
startSlot = submeshStartSlotIndex, | |
endSlot = drawOrderCount, | |
triangleCount = submeshTriangleCount, | |
firstVertex = submeshFirstVertex, | |
isLastSubmesh = true | |
} | |
); | |
mustUpdateMeshStructure = mustUpdateMeshStructure || | |
this.sharedMaterials.Length != workingSubmeshArguments.Count || // Material array changed in size | |
CheckIfMustUpdateMeshStructure(workingSubmeshArguments); // Submesh Argument Array changed. | |
// CheckIfMustUpdateMaterialArray (workingMaterials, sharedMaterials) | |
if (!mustUpdateMeshStructure) { | |
// Narrow phase material array check. | |
var workingMaterials = workingSubmeshArguments.Items; | |
for (int i = 0, n = sharedMaterials.Length; i < n; i++) { | |
if (this.sharedMaterials[i] != workingMaterials[i].material) { // Bounds check is implied above. | |
mustUpdateMeshStructure = true; | |
break; | |
} | |
} | |
} | |
// NOT ELSE | |
if (mustUpdateMeshStructure) { | |
this.submeshMaterials.Clear(); | |
for (int i = 0, n = workingSubmeshArguments.Count; i < n; i++) { | |
AddSubmesh(workingSubmeshArguments.Items[i], workingFlips); | |
} | |
// Set materials. | |
if (submeshMaterials.Count == sharedMaterials.Length) | |
submeshMaterials.CopyTo(sharedMaterials); | |
else | |
sharedMaterials = submeshMaterials.ToArray(); | |
meshRenderer.sharedMaterials = sharedMaterials; | |
} | |
// Ensure mesh data is the right size. | |
Vector3[] vertices = this.vertices; | |
bool newTriangles = vertexCount > vertices.Length; | |
if (newTriangles) { | |
// Not enough vertices, increase size. | |
this.vertices = vertices = new Vector3[vertexCount]; | |
this.colors = new Color32[vertexCount]; | |
this.uvs = new Vector2[vertexCount]; | |
mesh1.Clear(); | |
mesh2.Clear(); | |
meshState.stateMesh1.requiresUpdate = true; | |
meshState.stateMesh2.requiresUpdate = true; | |
} else { | |
// Too many vertices, zero the extra. | |
Vector3 zero = Vector3.zero; | |
for (int i = vertexCount, n = meshState.vertexCount; i < n; i++) | |
vertices[i] = zero; | |
} | |
meshState.vertexCount = vertexCount; | |
// Setup mesh. | |
float zSpacing = this.zSpacing; | |
float[] tempVertices = this.tempVertices; | |
Vector2[] uvs = this.uvs; | |
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 meshBoundsMax; | |
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 * (drawOrderCount - 1); | |
} else { | |
meshBoundsMin.z = zSpacing * (drawOrderCount - 1); | |
meshBoundsMax.z = 0f; | |
} | |
int i = 0; | |
do { | |
Slot slot = drawOrder.Items[i]; | |
Attachment attachment = slot.attachment; | |
RegionAttachment regionAttachment = attachment as RegionAttachment; | |
if (regionAttachment != null) { | |
regionAttachment.ComputeWorldVertices(slot.bone, tempVertices); | |
float z = i * zSpacing; | |
float x1 = tempVertices[RegionAttachment.X1], y1 = tempVertices[RegionAttachment.Y1]; | |
float x2 = tempVertices[RegionAttachment.X2], y2 = tempVertices[RegionAttachment.Y2]; | |
float x3 = tempVertices[RegionAttachment.X3], y3 = tempVertices[RegionAttachment.Y3]; | |
float x4 = tempVertices[RegionAttachment.X4], y4 = tempVertices[RegionAttachment.Y4]; | |
vertices[vertexIndex].x = x1; | |
vertices[vertexIndex].y = y1; | |
vertices[vertexIndex].z = z; | |
vertices[vertexIndex + 1].x = x4; | |
vertices[vertexIndex + 1].y = y4; | |
vertices[vertexIndex + 1].z = z; | |
vertices[vertexIndex + 2].x = x2; | |
vertices[vertexIndex + 2].y = y2; | |
vertices[vertexIndex + 2].z = z; | |
vertices[vertexIndex + 3].x = x3; | |
vertices[vertexIndex + 3].y = y3; | |
vertices[vertexIndex + 3].z = z; | |
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; | |
colors[vertexIndex] = color; | |
colors[vertexIndex + 1] = color; | |
colors[vertexIndex + 2] = color; | |
colors[vertexIndex + 3] = color; | |
float[] regionUVs = regionAttachment.uvs; | |
uvs[vertexIndex].x = regionUVs[RegionAttachment.X1]; | |
uvs[vertexIndex].y = regionUVs[RegionAttachment.Y1]; | |
uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4]; | |
uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4]; | |
uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2]; | |
uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2]; | |
uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3]; | |
uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3]; | |
// Calculate min/max X | |
if (x1 < meshBoundsMin.x) | |
meshBoundsMin.x = x1; | |
else if (x1 > meshBoundsMax.x) | |
meshBoundsMax.x = x1; | |
if (x2 < meshBoundsMin.x) | |
meshBoundsMin.x = x2; | |
else if (x2 > meshBoundsMax.x) | |
meshBoundsMax.x = x2; | |
if (x3 < meshBoundsMin.x) | |
meshBoundsMin.x = x3; | |
else if (x3 > meshBoundsMax.x) | |
meshBoundsMax.x = x3; | |
if (x4 < meshBoundsMin.x) | |
meshBoundsMin.x = x4; | |
else if (x4 > meshBoundsMax.x) | |
meshBoundsMax.x = x4; | |
// Calculate min/max Y | |
if (y1 < meshBoundsMin.y) | |
meshBoundsMin.y = y1; | |
else if (y1 > meshBoundsMax.y) | |
meshBoundsMax.y = y1; | |
if (y2 < meshBoundsMin.y) | |
meshBoundsMin.y = y2; | |
else if (y2 > meshBoundsMax.y) | |
meshBoundsMax.y = y2; | |
if (y3 < meshBoundsMin.y) | |
meshBoundsMin.y = y3; | |
else if (y3 > meshBoundsMax.y) | |
meshBoundsMax.y = y3; | |
if (y4 < meshBoundsMin.y) | |
meshBoundsMin.y = y4; | |
else if (y4 > meshBoundsMax.y) | |
meshBoundsMax.y = y4; | |
vertexIndex += 4; | |
} else { | |
if (!renderMeshes) | |
continue; | |
MeshAttachment meshAttachment = attachment as MeshAttachment; | |
if (meshAttachment != null) { | |
int meshVertexCount = meshAttachment.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; | |
float z = i * zSpacing; | |
for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { | |
float x = tempVertices[ii], y = tempVertices[ii + 1]; | |
vertices[vertexIndex].x = x; | |
vertices[vertexIndex].y = y; | |
vertices[vertexIndex].z = z; | |
colors[vertexIndex] = color; | |
uvs[vertexIndex].x = meshUVs[ii]; | |
uvs[vertexIndex].y = meshUVs[ii + 1]; | |
if (x < meshBoundsMin.x) | |
meshBoundsMin.x = x; | |
else if (x > meshBoundsMax.x) | |
meshBoundsMax.x = x; | |
if (y < meshBoundsMin.y) | |
meshBoundsMin.y = y; | |
else if (y > meshBoundsMax.y) | |
meshBoundsMax.y = y; | |
} | |
} else { | |
SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; | |
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; | |
float z = i * zSpacing; | |
for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { | |
float x = tempVertices[ii], y = tempVertices[ii + 1]; | |
vertices[vertexIndex].x = x; | |
vertices[vertexIndex].y = y; | |
vertices[vertexIndex].z = z; | |
colors[vertexIndex] = color; | |
uvs[vertexIndex].x = meshUVs[ii]; | |
uvs[vertexIndex].y = meshUVs[ii + 1]; | |
if (x < meshBoundsMin.x) | |
meshBoundsMin.x = x; | |
else if (x > meshBoundsMax.x) | |
meshBoundsMax.x = x; | |
if (y < meshBoundsMin.y) | |
meshBoundsMin.y = y; | |
else if (y > meshBoundsMax.y) | |
meshBoundsMax.y = y; | |
} | |
} | |
} | |
} | |
} while (++i < drawOrderCount); | |
} | |
// Double buffer mesh. | |
Mesh mesh = useMesh1 ? mesh1 : mesh2; | |
meshFilter.sharedMesh = mesh; | |
mesh.vertices = vertices; | |
mesh.colors32 = colors; | |
mesh.uv = uvs; | |
if (mustUpdateMeshStructure) { | |
int submeshCount = submeshMaterials.Count; | |
mesh.subMeshCount = submeshCount; | |
for (int i = 0; i < submeshCount; ++i) | |
mesh.SetTriangles(submeshes.Items[i].triangles, i); | |
// Done updating mesh. | |
storedState.requiresUpdate = false; | |
} | |
Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin; | |
Vector3 meshBoundsCenter = meshBoundsMin + meshBoundsExtents * 0.5f; | |
mesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents); | |
if (newTriangles && calculateNormals) { | |
Vector3[] normals = new Vector3[vertexCount]; | |
Vector3 normal = new Vector3(0, 0, -1); | |
for (int i = 0; i < vertexCount; i++) | |
normals[i] = normal; | |
(useMesh1 ? mesh2 : mesh1).vertices = vertices; // Set other mesh vertices. | |
mesh1.normals = normals; | |
mesh2.normals = normals; | |
if (calculateTangents) { | |
Vector4[] tangents = new Vector4[vertexCount]; | |
Vector3 tangent = new Vector3(0, 0, 1); | |
for (int i = 0; i < vertexCount; i++) | |
tangents[i] = tangent; | |
mesh1.tangents = tangents; | |
mesh2.tangents = tangents; | |
} | |
} | |
// Update previous state | |
storedState.immutableTriangles = immutableTriangles; | |
storedAttachments.Clear(true); | |
storedAttachments.GrowIfNeeded(workingAttachments.Capacity); | |
storedAttachments.Count = workingAttachments.Count; | |
workingAttachments.CopyTo(storedAttachments.Items); | |
storedFlips.GrowIfNeeded(workingFlips.Capacity); | |
storedFlips.Count = workingFlips.Count; | |
workingFlips.CopyTo(storedFlips.Items); | |
storedState.addSubmeshArguments.GrowIfNeeded(workingSubmeshArguments.Capacity); | |
storedState.addSubmeshArguments.Count = workingSubmeshArguments.Count; | |
workingSubmeshArguments.CopyTo(storedState.addSubmeshArguments.Items); | |
// Submesh Renderers | |
if (submeshRenderers.Length > 0) { | |
for (int i = 0; i < submeshRenderers.Length; i++) { | |
SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i]; | |
if (submeshRenderer.submeshIndex < sharedMaterials.Length) { | |
submeshRenderer.SetMesh(meshRenderer, useMesh1 ? mesh1 : mesh2, sharedMaterials[submeshRenderer.submeshIndex]); | |
} else { | |
submeshRenderer.GetComponent<Renderer>().enabled = false; | |
} | |
} | |
} | |
useMesh1 = !useMesh1; | |
} | |
private bool CheckIfMustUpdateMeshStructure (ExposedList<MeshState.AddSubmeshArguments> workingAddSubmeshArguments) { | |
#if UNITY_EDITOR | |
if (!Application.isPlaying) | |
return true; | |
#endif | |
// Check if any mesh settings were changed | |
MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; | |
// Check if submesh structures has changed | |
ExposedList<MeshState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments; | |
int submeshCount = workingAddSubmeshArguments.Count; | |
if (addSubmeshArgumentsCurrentMesh.Count != submeshCount) | |
return true; | |
for (int i = 0; i < submeshCount; i++) { | |
if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref workingAddSubmeshArguments.Items[i])) | |
return true; | |
} | |
return false; | |
} | |
private void AddSubmesh (MeshState.AddSubmeshArguments submeshArguments, ExposedList<bool> flipStates) { | |
int submeshIndex = submeshMaterials.Count; | |
submeshMaterials.Add(submeshArguments.material); | |
if (submeshes.Count <= submeshIndex) | |
submeshes.Add(new Submesh()); | |
else if (immutableTriangles) | |
return; | |
Submesh submesh = submeshes.Items[submeshIndex]; | |
int[] triangles = submesh.triangles; | |
int triangleCount = submeshArguments.triangleCount; | |
int firstVertex = submeshArguments.firstVertex; | |
int trianglesCapacity = triangles.Length; | |
if (submeshArguments.isLastSubmesh && trianglesCapacity > triangleCount) { | |
// Last submesh may have more triangles than required, so zero triangles to the end. | |
for (int i = triangleCount; i < trianglesCapacity; i++) | |
triangles[i] = 0; | |
submesh.triangleCount = triangleCount; | |
} else if (trianglesCapacity != triangleCount) { | |
// Reallocate triangles when not the exact size needed. | |
submesh.triangles = triangles = new int[triangleCount]; | |
submesh.triangleCount = 0; | |
} | |
if (!renderMeshes && !frontFacing) { | |
// Use stored triangles if possible. | |
if (submesh.firstVertex != firstVertex || submesh.triangleCount < triangleCount) { | |
submesh.triangleCount = triangleCount; | |
submesh.firstVertex = firstVertex; | |
//int drawOrderIndex = 0; | |
for (int i = 0; i < triangleCount; i += 6, firstVertex += 4/*, drawOrderIndex++*/) { | |
triangles[i] = firstVertex; | |
triangles[i + 1] = firstVertex + 2; | |
triangles[i + 2] = firstVertex + 1; | |
triangles[i + 3] = firstVertex + 2; | |
triangles[i + 4] = firstVertex + 3; | |
triangles[i + 5] = firstVertex + 1; | |
} | |
} | |
return; | |
} | |
// Iterate through all slots and store their triangles. | |
ExposedList<Slot> drawOrder = skeleton.DrawOrder; | |
int triangleIndex = 0; // Modified by loop | |
for (int i = submeshArguments.startSlot, n = submeshArguments.endSlot; i < n; i++) { | |
Slot slot = drawOrder.Items[i]; | |
Attachment attachment = slot.attachment; | |
bool flip = flipStates.Items[i]; | |
// Add RegionAttachment triangles | |
if (attachment is RegionAttachment) { | |
if (!flip) { | |
triangles[triangleIndex] = firstVertex; | |
triangles[triangleIndex + 1] = firstVertex + 2; | |
triangles[triangleIndex + 2] = firstVertex + 1; | |
triangles[triangleIndex + 3] = firstVertex + 2; | |
triangles[triangleIndex + 4] = firstVertex + 3; | |
triangles[triangleIndex + 5] = firstVertex + 1; | |
} else { | |
triangles[triangleIndex] = firstVertex + 1; | |
triangles[triangleIndex + 1] = firstVertex + 2; | |
triangles[triangleIndex + 2] = firstVertex; | |
triangles[triangleIndex + 3] = firstVertex + 1; | |
triangles[triangleIndex + 4] = firstVertex + 3; | |
triangles[triangleIndex + 5] = firstVertex + 2; | |
} | |
triangleIndex += 6; | |
firstVertex += 4; | |
continue; | |
} | |
// Add (Skinned)MeshAttachment triangles | |
int[] attachmentTriangles; | |
int attachmentVertexCount; | |
MeshAttachment meshAttachment = attachment as MeshAttachment; | |
if (meshAttachment != null) { | |
attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2 | |
attachmentTriangles = meshAttachment.triangles; | |
} else { | |
SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; | |
if (skinnedMeshAttachment != null) { | |
attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; // length/2 | |
attachmentTriangles = skinnedMeshAttachment.triangles; | |
} else | |
continue; | |
} | |
if (flip) { | |
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3) { | |
triangles[triangleIndex + 2] = firstVertex + attachmentTriangles[ii]; | |
triangles[triangleIndex + 1] = firstVertex + attachmentTriangles[ii + 1]; | |
triangles[triangleIndex] = firstVertex + attachmentTriangles[ii + 2]; | |
} | |
} else { | |
for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) { | |
triangles[triangleIndex] = firstVertex + attachmentTriangles[ii]; | |
} | |
} | |
firstVertex += attachmentVertexCount; | |
} | |
} | |
#if UNITY_EDITOR | |
void OnDrawGizmos () { | |
// Make selection easier by drawing a clear gizmo over the skeleton. | |
meshFilter = GetComponent<MeshFilter>(); | |
if (meshFilter == null) return; | |
Mesh mesh = meshFilter.sharedMesh; | |
if (mesh == null) return; | |
Bounds meshBounds = mesh.bounds; | |
Gizmos.color = Color.clear; | |
Gizmos.matrix = transform.localToWorldMatrix; | |
Gizmos.DrawCube(meshBounds.center, meshBounds.size); | |
} | |
#endif | |
private class MeshState { | |
public int vertexCount; | |
public readonly SingleMeshState buffer = new SingleMeshState(); | |
public readonly SingleMeshState stateMesh1 = new SingleMeshState(); | |
public readonly SingleMeshState stateMesh2 = new SingleMeshState(); | |
public class SingleMeshState { | |
public bool immutableTriangles; | |
public bool requiresUpdate; | |
public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>(); | |
public readonly ExposedList<bool> attachmentsFlipState = new ExposedList<bool>(); | |
public readonly ExposedList<AddSubmeshArguments> addSubmeshArguments = new ExposedList<AddSubmeshArguments>(); | |
public void UpdateAttachmentCount (int attachmentCount) { | |
attachmentsFlipState.GrowIfNeeded(attachmentCount); | |
attachmentsFlipState.Count = attachmentCount; | |
attachments.GrowIfNeeded(attachmentCount); | |
attachments.Count = attachmentCount; | |
} | |
} | |
public struct AddSubmeshArguments { | |
public Material material; | |
public int startSlot; | |
public int endSlot; | |
public int triangleCount; | |
public int firstVertex; | |
public bool isLastSubmesh; | |
public bool Equals (ref AddSubmeshArguments other) { | |
return | |
//!ReferenceEquals(material, null) && | |
//!ReferenceEquals(other.material, null) && | |
//material.GetInstanceID() == other.material.GetInstanceID() && | |
startSlot == other.startSlot && | |
endSlot == other.endSlot && | |
triangleCount == other.triangleCount && | |
firstVertex == other.firstVertex; | |
} | |
} | |
} | |
} | |
class Submesh { | |
public int[] triangles = new int[0]; | |
public int triangleCount; | |
public int firstVertex = -1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment