Cleanup and improved inspector workflow.

This commit is contained in:
pharan 2016-03-20 00:49:13 +08:00
parent 9e8fdeef6c
commit 80d8e9e276
15 changed files with 467 additions and 151 deletions

View File

@ -51,6 +51,7 @@ namespace Spine.Unity {
SpineEditorUtilities.ConfirmInitialization(); SpineEditorUtilities.ConfirmInitialization();
atlasFile = serializedObject.FindProperty("atlasFile"); atlasFile = serializedObject.FindProperty("atlasFile");
materials = serializedObject.FindProperty("materials"); materials = serializedObject.FindProperty("materials");
materials.isExpanded = true;
atlasAsset = (AtlasAsset)target; atlasAsset = (AtlasAsset)target;
UpdateBakedList(); UpdateBakedList();
} }

View File

@ -39,7 +39,7 @@ namespace Spine.Unity {
public class SkeletonRendererInspector : Editor { public class SkeletonRendererInspector : Editor {
protected static bool advancedFoldout; protected static bool advancedFoldout;
protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, submeshSeparators, front, zSpacing; protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, separatorSlotNames, front, zSpacing;
protected SpineInspectorUtility.SerializedSortingProperties sortingProperties; protected SpineInspectorUtility.SerializedSortingProperties sortingProperties;
@ -51,7 +51,9 @@ namespace Spine.Unity {
tangents = serializedObject.FindProperty("calculateTangents"); tangents = serializedObject.FindProperty("calculateTangents");
meshes = serializedObject.FindProperty("renderMeshes"); meshes = serializedObject.FindProperty("renderMeshes");
immutableTriangles = serializedObject.FindProperty("immutableTriangles"); immutableTriangles = serializedObject.FindProperty("immutableTriangles");
submeshSeparators = serializedObject.FindProperty("submeshSeparators"); separatorSlotNames = serializedObject.FindProperty("separatorSlotNames");
separatorSlotNames.isExpanded = true;
front = serializedObject.FindProperty("frontFacing"); front = serializedObject.FindProperty("frontFacing");
zSpacing = serializedObject.FindProperty("zSpacing"); zSpacing = serializedObject.FindProperty("zSpacing");
@ -108,36 +110,49 @@ namespace Spine.Unity {
// More Render Options... // More Render Options...
{ {
advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced"); using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
if(advancedFoldout) {
EditorGUI.indentLevel++; EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(submeshSeparators, true); advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
EditorGUILayout.Space(); if(advancedFoldout) {
EditorGUILayout.PropertyField(meshes, EditorGUI.indentLevel++;
new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments")); SeparatorsField(separatorSlotNames);
EditorGUILayout.PropertyField(immutableTriangles, EditorGUILayout.PropertyField(meshes,
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility")); new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"));
EditorGUILayout.Space(); EditorGUILayout.PropertyField(immutableTriangles,
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
EditorGUILayout.Space();
const float MinZSpacing = -0.1f; const float MinZSpacing = -0.1f;
const float MaxZSpacing = 0f; const float MaxZSpacing = 0f;
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing); EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing);
if (normals != null) { if (normals != null) {
EditorGUILayout.PropertyField(normals); EditorGUILayout.PropertyField(normals);
EditorGUILayout.PropertyField(tangents); EditorGUILayout.PropertyField(tangents);
}
if (front != null) {
EditorGUILayout.PropertyField(front);
}
EditorGUI.indentLevel--;
} }
if (front != null) {
EditorGUILayout.PropertyField(front);
}
EditorGUILayout.Separator();
EditorGUI.indentLevel--; EditorGUI.indentLevel--;
} }
} }
} }
public static void SeparatorsField (SerializedProperty separatorSlotNames) {
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
if (separatorSlotNames.isExpanded) {
EditorGUILayout.PropertyField(separatorSlotNames, includeChildren: true);
} else {
EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + string.Format(" [{0}]", separatorSlotNames.arraySize)), includeChildren: true);
}
}
}
override public void OnInspectorGUI () { override public void OnInspectorGUI () {
serializedObject.Update(); serializedObject.Update();
DrawInspectorGUI(); DrawInspectorGUI();

View File

@ -33,10 +33,19 @@ using UnityEngine;
using System.Collections; using System.Collections;
using UnityEditor; using UnityEditor;
using System.Reflection; using System.Reflection;
using System;
namespace Spine.Unity { namespace Spine.Unity {
public static class SpineInspectorUtility { public static class SpineInspectorUtility {
public static string Pluralize (int n, string singular, string plural) {
return n + " " + (n == 1 ? singular : plural);
}
public static string PluralThenS (int n) {
return n == 1 ? "" : "s";
}
#region Sorting Layer Field Helpers #region Sorting Layer Field Helpers
static readonly GUIContent SortingLayerLabel = new GUIContent("Sorting Layer"); static readonly GUIContent SortingLayerLabel = new GUIContent("Sorting Layer");
static readonly GUIContent OrderInLayerLabel = new GUIContent("Order in Layer"); static readonly GUIContent OrderInLayerLabel = new GUIContent("Order in Layer");
@ -45,7 +54,7 @@ namespace Spine.Unity {
static MethodInfo SortingLayerFieldMethod { static MethodInfo SortingLayerFieldMethod {
get { get {
if (m_SortingLayerFieldMethod == null) if (m_SortingLayerFieldMethod == null)
m_SortingLayerFieldMethod = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new System.Type[] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null); m_SortingLayerFieldMethod = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new [] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null);
return m_SortingLayerFieldMethod; return m_SortingLayerFieldMethod;
} }

View File

@ -28,6 +28,7 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
#define SPINE_OPTIONAL_NORMALS
using UnityEngine; using UnityEngine;
namespace Spine.Unity.MeshGeneration { namespace Spine.Unity.MeshGeneration {
@ -42,6 +43,33 @@ namespace Spine.Unity.MeshGeneration {
protected Color32[] meshColors32; protected Color32[] meshColors32;
protected Vector2[] meshUVs; protected Vector2[] meshUVs;
protected bool generateNormals = false;
public bool GenerateNormals {
get { return generateNormals; }
set { generateNormals = value; }
}
Vector3[] meshNormals;
public void TryAddNormalsTo (Mesh mesh, int targetVertexCount) {
#if SPINE_OPTIONAL_NORMALS
if (generateNormals) {
bool verticesWasResized = this.meshNormals == null || targetVertexCount > meshNormals.Length;
if (verticesWasResized) {
this.meshNormals = new Vector3[targetVertexCount];
Vector3 normal = new Vector3(0, 0, -1);
Vector3[] normals = this.meshNormals;
for (int i = 0; i < targetVertexCount; i++)
normals[i] = normal;
}
mesh.normals = this.meshNormals;
}
#endif
}
public static bool EnsureSize (int targetVertexCount, ref Vector3[] vertices, ref Vector2[] uvs, ref Color32[] colors) { public static bool EnsureSize (int targetVertexCount, ref Vector3[] vertices, ref Vector2[] uvs, ref Color32[] colors) {
Vector3[] verts = vertices; Vector3[] verts = vertices;
bool verticesWasResized = verts == null || targetVertexCount > verts.Length; bool verticesWasResized = verts == null || targetVertexCount > verts.Length;
@ -247,23 +275,20 @@ namespace Spine.Unity.MeshGeneration {
/// <param name="triangleBuffer">The triangle buffer array to be filled. This reference will be replaced in case the triangle values don't fit.</param> /// <param name="triangleBuffer">The triangle buffer array to be filled. This reference will be replaced in case the triangle values don't fit.</param>
/// <param name="bufferTriangleCount">The current triangle count of the submesh buffer. This is not always equal to triangleBuffer.Length because for last submeshes, length may be larger than needed.</param> /// <param name="bufferTriangleCount">The current triangle count of the submesh buffer. This is not always equal to triangleBuffer.Length because for last submeshes, length may be larger than needed.</param>
/// <param name="isLastSubmesh">If set to <c>true</c>, the triangle buffer is allowed to be larger than needed.</param> /// <param name="isLastSubmesh">If set to <c>true</c>, the triangle buffer is allowed to be larger than needed.</param>
public static void FillTriangles (Skeleton skeleton, int triangleCount, int firstVertex, int startSlot, int endSlot, ref int[] triangleBuffer, ref int bufferTriangleCount, bool isLastSubmesh) { public static void FillTriangles (Skeleton skeleton, int triangleCount, int firstVertex, int startSlot, int endSlot, ref int[] triangleBuffer, bool isLastSubmesh) {
int trianglesCapacity = triangleBuffer.Length; int trianglesCapacity = triangleBuffer.Length;
var tris = triangleBuffer; var tris = triangleBuffer;
// Ensure triangleBuffer size. // Ensure triangleBuffer size.
if (isLastSubmesh) { if (isLastSubmesh) {
bufferTriangleCount = triangleCount;
if (trianglesCapacity > triangleCount) { if (trianglesCapacity > triangleCount) {
for (int i = triangleCount; i < trianglesCapacity; i++) for (int i = triangleCount; i < trianglesCapacity; i++)
tris[i] = 0; tris[i] = 0;
} else if (trianglesCapacity < triangleCount) { } else if (trianglesCapacity < triangleCount) {
triangleBuffer = tris = new int[triangleCount]; triangleBuffer = tris = new int[triangleCount];
bufferTriangleCount = 0;
} }
} else if (trianglesCapacity != triangleCount) { } else if (trianglesCapacity != triangleCount) {
triangleBuffer = tris = new int[triangleCount]; triangleBuffer = tris = new int[triangleCount];
bufferTriangleCount = 0;
} }
// Iterate through submesh slots and store the triangles. // Iterate through submesh slots and store the triangles.
@ -313,11 +338,10 @@ namespace Spine.Unity.MeshGeneration {
#region SubmeshTriangleBuffer #region SubmeshTriangleBuffer
public class SubmeshTriangleBuffer { public class SubmeshTriangleBuffer {
public int[] triangles; public int[] triangles;
public int triangleCount; //public int triangleCount;
public SubmeshTriangleBuffer (int triangleCount) { public SubmeshTriangleBuffer (int triangleCount) {
triangles = new int[triangleCount]; triangles = new int[triangleCount];
this.triangleCount = triangleCount;
} }
} }
#endregion #endregion

View File

@ -116,7 +116,7 @@ namespace Spine.Unity.MeshGeneration {
} }
// Step 4 : Update Triangles buffer // Step 4 : Update Triangles buffer
ArraysMeshGenerator.FillTriangles(skeleton, totalTriangleCount, 0, 0, drawOrderCount, ref this.triangles, ref this.triangleBufferCount, true); ArraysMeshGenerator.FillTriangles(skeleton, totalTriangleCount, 0, 0, drawOrderCount, ref this.triangles, true);
// Step 5 : Update Mesh with buffers // Step 5 : Update Mesh with buffers
var mesh = doubleBufferedMesh.GetNextMesh(); var mesh = doubleBufferedMesh.GetNextMesh();

View File

@ -28,6 +28,7 @@
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
#define SPINE_OPTIONAL_NORMALS
using UnityEngine; using UnityEngine;
namespace Spine.Unity.MeshGeneration { namespace Spine.Unity.MeshGeneration {
@ -107,7 +108,7 @@ namespace Spine.Unity.MeshGeneration {
if (structureDoesntMatch) { if (structureDoesntMatch) {
var currentBuffer = submeshBuffers.Items[submeshIndex]; var currentBuffer = submeshBuffers.Items[submeshIndex];
bool isLastSubmesh = (submeshIndex == submeshCount - 1); bool isLastSubmesh = (submeshIndex == submeshCount - 1);
ArraysMeshGenerator.FillTriangles(skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, startSlot, endSlot, ref currentBuffer.triangles, ref currentBuffer.triangleCount, isLastSubmesh); ArraysMeshGenerator.FillTriangles(skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, startSlot, endSlot, ref currentBuffer.triangles, isLastSubmesh);
} }
} }
@ -119,6 +120,9 @@ namespace Spine.Unity.MeshGeneration {
// STEP 3: Assign the buffers into the Mesh. // STEP 3: Assign the buffers into the Mesh.
smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, currentAttachments, currentInstructions); smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, currentAttachments, currentInstructions);
mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax); mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
#if SPINE_OPTIONAL_NORMALS
this.TryAddNormalsTo(mesh, vertexCount);
#endif
if (structureDoesntMatch) { if (structureDoesntMatch) {
// Push new triangles if doesn't match. // Push new triangles if doesn't match.

View File

@ -202,7 +202,7 @@ namespace Spine.Unity.MeshGeneration {
if (structureDoesntMatch) { if (structureDoesntMatch) {
var currentBuffer = submeshBuffers.Items[submeshIndex]; var currentBuffer = submeshBuffers.Items[submeshIndex];
bool isLastSubmesh = (submeshIndex == submeshCount - 1); bool isLastSubmesh = (submeshIndex == submeshCount - 1);
ArraysMeshGenerator.FillTriangles(skeleton, submeshInstruction.triangleCount, submeshInstruction.firstVertexIndex, start, end, ref currentBuffer.triangles, ref currentBuffer.triangleCount, isLastSubmesh); ArraysMeshGenerator.FillTriangles(skeleton, submeshInstruction.triangleCount, submeshInstruction.firstVertexIndex, start, end, ref currentBuffer.triangles, isLastSubmesh);
} }
} }

View File

@ -22,6 +22,7 @@ namespace Spine.Unity.MeshGeneration {
// Step 4: Put the Mesh in MeshFilter. Put the Materials in MeshRenderer.sharedMaterials. // Step 4: Put the Mesh in MeshFilter. Put the Materials in MeshRenderer.sharedMaterials.
public interface ISubmeshSetMeshGenerator { public interface ISubmeshSetMeshGenerator {
MeshAndMaterials GenerateMesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh); MeshAndMaterials GenerateMesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh);
bool GenerateNormals { get; set; }
} }
/// <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>

View File

@ -3,7 +3,7 @@ using System.Collections;
using UnityEditor; using UnityEditor;
using Spine.Unity; using Spine.Unity;
[CustomEditor(typeof(SkeletonRenderPart))] [CustomEditor(typeof(SkeletonPartsRenderer))]
public class SkeletonRenderPartInspector : Editor { public class SkeletonRenderPartInspector : Editor {
SpineInspectorUtility.SerializedSortingProperties sortingProperties; SpineInspectorUtility.SerializedSortingProperties sortingProperties;

View File

@ -1,57 +1,224 @@
using UnityEngine; using UnityEngine;
using System.Collections;
using UnityEditor; using UnityEditor;
using Spine.Unity;
[CustomEditor(typeof(SkeletonRenderSeparator))] namespace Spine.Unity {
public class SkeletonRenderSeparatorInspector : Editor {
[CustomEditor(typeof(SkeletonRenderSeparator))]
public class SkeletonRenderSeparatorInspector : Editor {
SkeletonRenderSeparator component;
SkeletonRenderSeparator component; // Properties
SerializedProperty skeletonRenderer_, copyPropertyBlock_, copyMeshRendererFlags_, partsRenderers_;
void OnEnable () { // For separator field.
component = target as SkeletonRenderSeparator; SerializedObject skeletonRendererSerializedObject;
} SerializedProperty separatorNamesProp;
bool separatorExpanded = true;
System.Func<int, string, string, string> Plural = SpineInspectorUtility.Pluralize;
public override void OnInspectorGUI () { void OnEnable () {
base.OnInspectorGUI(); if (component == null)
component = target as SkeletonRenderSeparator;
if (GUILayout.Button("Add Renderer")) { skeletonRenderer_ = serializedObject.FindProperty("skeletonRenderer");
const int SortingOrderIncrement = 5; copyPropertyBlock_ = serializedObject.FindProperty("copyPropertyBlock");
int index = component.renderers.Count; copyMeshRendererFlags_ = serializedObject.FindProperty("copyMeshRendererFlags");
var smr = SkeletonRenderPart.NewSubmeshRendererGameObject(component.transform, index.ToString()); partsRenderers_ = serializedObject.FindProperty("partsRenderers");
component.renderers.Add(smr); partsRenderers_.isExpanded = true;
}
// increment renderer sorting order. public override void OnInspectorGUI () {
if (index != 0) { // TODO: Add Undo support
var prev = component.renderers[index - 1]; var componentRenderers = component.partsRenderers;
if (prev != null) { int separatorCount = 0;
var prevMeshRenderer = prev.GetComponent<MeshRenderer>(); int totalParts;
var currentMeshRenderer = smr.GetComponent<MeshRenderer>();
if (prevMeshRenderer != null && currentMeshRenderer != null) {
int prevSortingLayer = prevMeshRenderer.sortingLayerID;
int prevSortingOrder = prevMeshRenderer.sortingOrder;
currentMeshRenderer.sortingLayerID = prevSortingLayer; bool componentEnabled = component.enabled;
currentMeshRenderer.sortingOrder = prevSortingOrder + SortingOrderIncrement; bool checkBox = EditorGUILayout.Toggle("Enable Separator", componentEnabled);
if (checkBox != componentEnabled) {
component.enabled = checkBox;
}
EditorGUILayout.PropertyField(copyPropertyBlock_);
EditorGUILayout.PropertyField(copyMeshRendererFlags_);
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
// Fancy SkeletonRenderer foldout reference field
{
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
var foldoutSkeletonRendererRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(foldoutSkeletonRendererRect, skeletonRenderer_);
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
if (component.SkeletonRenderer != null) {
separatorExpanded = EditorGUI.Foldout(foldoutSkeletonRendererRect, separatorExpanded, "");
}
EditorGUI.indentLevel--;
}
EditorGUI.BeginChangeCheck();
if (component.SkeletonRenderer != null) {
// SubmeshSeparators from SkeletonRenderer
{
bool skeletonRendererMismatch = skeletonRendererSerializedObject != null && skeletonRendererSerializedObject.targetObject != component.SkeletonRenderer;
if (separatorNamesProp == null || skeletonRendererMismatch) {
if (component.SkeletonRenderer != null) {
skeletonRendererSerializedObject = new SerializedObject(component.SkeletonRenderer);
separatorNamesProp = skeletonRendererSerializedObject.FindProperty("separatorSlotNames");
separatorNamesProp.isExpanded = true;
}
}
if (separatorNamesProp != null) {
if (separatorExpanded) {
EditorGUI.indentLevel++;
SkeletonRendererInspector.SeparatorsField(separatorNamesProp);
EditorGUI.indentLevel--;
}
if (Application.isPlaying)
separatorCount = component.SkeletonRenderer.separatorSlots.Count;
else
separatorCount = separatorNamesProp.arraySize;
}
}
if (separatorCount == 0) {
EditorGUILayout.HelpBox("Separators are empty. Change the size to 1 and choose a slot if you want the render to be separated.", MessageType.Info);
}
}
if (EditorGUI.EndChangeCheck())
skeletonRendererSerializedObject.ApplyModifiedProperties();
totalParts = separatorCount + 1;
var counterStyle = separatorExpanded ? EditorStyles.label : EditorStyles.miniLabel;
EditorGUILayout.LabelField(string.Format("{0}: separates into {1}.", Plural(separatorCount, "separator", "separators"), Plural(totalParts, "part", "parts") ), counterStyle);
}
// Parts renderers
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(this.partsRenderers_, true);
EditorGUI.indentLevel--;
// (Button) Match Separators count
if (separatorNamesProp != null) {
int currentRenderers = 0;
foreach (var r in componentRenderers) {
if (r != null)
currentRenderers++;
}
int extraRenderersNeeded = totalParts - currentRenderers;
if (component.enabled && component.SkeletonRenderer != null && extraRenderersNeeded > 0) {
EditorGUILayout.HelpBox(string.Format("Insufficient parts renderers. Some parts will not be rendered."), MessageType.Warning);
string addMissingLabel = string.Format("Add the missing renderer{1} ({0}) ", extraRenderersNeeded, SpineInspectorUtility.PluralThenS(extraRenderersNeeded));
//var addMissingContentButtonContent = new GUIContent("Add", GUIUtility.)
if (GUILayout.Button(addMissingLabel, GUILayout.Height(40f))) {
AddPartsRenderer(extraRenderersNeeded);
DetectOrphanedPartsRenderers(component);
}
}
}
using (new EditorGUILayout.HorizontalScope()) {
// (Button) Destroy Renderers button
if (componentRenderers.Count > 0) {
if (GUILayout.Button("Clear Parts Renderers")) {
// Do you really want to destroy all?
if (EditorUtility.DisplayDialog("Destroy Renderers", "Do you really want to destroy all the Parts Renderer GameObjects in the list? (Undo will not work.)", "Destroy", "Cancel")) {
foreach (var r in componentRenderers) {
if (r != null)
DestroyImmediate(r.gameObject, allowDestroyingAssets: false);
}
componentRenderers.Clear();
// Do you also want to destroy orphans? (You monster.)
DetectOrphanedPartsRenderers(component);
}
}
}
// (Button) Add Part Renderer button
if (GUILayout.Button("Add (1) Parts Renderer"))
AddPartsRenderer(1);
}
}
serializedObject.ApplyModifiedProperties();
}
public void AddPartsRenderer (int count) {
var componentRenderers = component.partsRenderers;
bool emptyFound = componentRenderers.Exists(x => x == null);
if (emptyFound) {
bool userClearEntries = EditorUtility.DisplayDialog("Empty entries found", "Null entries found. Do you want to remove null entries before adding the new renderer? ", "Clear Empty Entries", "Don't Clear");
if (userClearEntries) componentRenderers.RemoveAll(x => x == null);
}
for (int i = 0; i < count; i++) {
int index = componentRenderers.Count;
var smr = SkeletonPartsRenderer.NewPartsRendererGameObject(component.transform, index.ToString());
componentRenderers.Add(smr);
EditorGUIUtility.PingObject(smr);
// increment renderer sorting order.
if (index != 0) {
var prev = componentRenderers[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 + SkeletonRenderSeparator.DefaultSortingOrderIncrement;
}
}
}
}
}
/// <summary>Detects orphaned parts renderers and offers to delete them.</summary>
public void DetectOrphanedPartsRenderers (SkeletonRenderSeparator component) {
var children = component.GetComponentsInChildren<SkeletonPartsRenderer>();
var orphans = new System.Collections.Generic.List<SkeletonPartsRenderer>();
foreach (var r in children) {
if (!component.partsRenderers.Contains(r)) {
orphans.Add(r);
}
}
if (orphans.Count > 0) {
if (EditorUtility.DisplayDialog("Destroy Submesh Renderers", "Unassigned renderers were found. Do you want to delete them? (These may belong to another Render Separator in the same hierarchy. If you don't have another Render Separator component in the children of this GameObject, it's likely safe to delete. Warning: This operation cannot be undone.)", "Delete", "Cancel")) {
foreach (var o in orphans) {
DestroyImmediate(o.gameObject, allowDestroyingAssets: false);
} }
} }
} }
} }
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(); #region SkeletonRenderer Context Menu Item
} [MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator")]
static void AddRenderSeparatorComponent (MenuCommand cmd) {
var skeletonRenderer = cmd.context as SkeletonRenderer;
skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
} }
// Validate
[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator", true)]
static bool ValidateAddRenderSeparatorComponent (MenuCommand cmd) {
var skeletonRenderer = cmd.context as SkeletonRenderer;
var separator = skeletonRenderer.GetComponent<SkeletonRenderSeparator>();
bool separatorNotOnObject = separator == null;
return separatorNotOnObject;
}
#endregion
} }
} }

View File

@ -4,7 +4,9 @@ using Spine.Unity.MeshGeneration;
namespace Spine.Unity { namespace Spine.Unity {
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class SkeletonRenderPart : MonoBehaviour { public class SkeletonPartsRenderer : MonoBehaviour {
#region Properties
ISubmeshSetMeshGenerator meshGenerator; ISubmeshSetMeshGenerator meshGenerator;
public ISubmeshSetMeshGenerator MeshGenerator { public ISubmeshSetMeshGenerator MeshGenerator {
get { get {
@ -28,6 +30,7 @@ namespace Spine.Unity {
return meshFilter; return meshFilter;
} }
} }
#endregion
void LazyIntialize () { void LazyIntialize () {
if (meshGenerator != null) return; if (meshGenerator != null) return;
@ -37,10 +40,11 @@ namespace Spine.Unity {
} }
public void ClearMesh () { public void ClearMesh () {
LazyIntialize();
meshFilter.sharedMesh = null; meshFilter.sharedMesh = null;
} }
public void RenderSubmesh (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) { public void RenderParts (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
LazyIntialize(); LazyIntialize();
MeshAndMaterials m = meshGenerator.GenerateMesh(instructions, startSubmesh, endSubmesh); MeshAndMaterials m = meshGenerator.GenerateMesh(instructions, startSubmesh, endSubmesh);
meshFilter.sharedMesh = m.mesh; meshFilter.sharedMesh = m.mesh;
@ -52,10 +56,10 @@ namespace Spine.Unity {
meshRenderer.SetPropertyBlock(block); meshRenderer.SetPropertyBlock(block);
} }
public static SkeletonRenderPart NewSubmeshRendererGameObject (Transform parent, string name) { public static SkeletonPartsRenderer NewPartsRendererGameObject (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<SkeletonRenderPart>(); var returnComponent = go.AddComponent<SkeletonPartsRenderer>();
return returnComponent; return returnComponent;
} }

View File

@ -4,8 +4,13 @@ using System.Collections.Generic;
using Spine.Unity; using Spine.Unity;
namespace Spine.Unity { namespace Spine.Unity {
[HelpURL("")]
[ExecuteInEditMode] [ExecuteInEditMode]
public class SkeletonRenderSeparator : MonoBehaviour { public class SkeletonRenderSeparator : MonoBehaviour {
public const int DefaultSortingOrderIncrement = 5;
#region Inspector
[SerializeField] [SerializeField]
protected SkeletonRenderer skeletonRenderer; protected SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer { public SkeletonRenderer SkeletonRenderer {
@ -13,32 +18,29 @@ namespace Spine.Unity {
set { set {
if (skeletonRenderer != null) if (skeletonRenderer != null)
skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender; skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender;
skeletonRenderer = value; skeletonRenderer = value;
this.enabled = false; // Disable if nulled.
} }
} }
MeshRenderer masterMeshRenderer; MeshRenderer mainMeshRenderer;
public bool copyPropertyBlock = false;
[Header("Settings")] public bool copyMeshRendererFlags = false;
public bool propagateMaterialPropertyBlock = false; public List<Spine.Unity.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
public bool controlMainMeshRenderer = true;
[Space(10f)]
public List<Spine.Unity.SkeletonRenderPart> renderers = new List<SkeletonRenderPart>();
#if UNITY_EDITOR
void Reset () { void Reset () {
if (skeletonRenderer == null) { if (skeletonRenderer == null)
skeletonRenderer = GetComponent<SkeletonRenderer>(); skeletonRenderer = GetComponent<SkeletonRenderer>();
}
} }
#endif
#endregion
void OnEnable () { void OnEnable () {
if (skeletonRenderer == null) return; if (skeletonRenderer == null) return;
if (block == null) block = new MaterialPropertyBlock(); if (block == null) block = new MaterialPropertyBlock();
masterMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>(); mainMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
if (controlMainMeshRenderer)
masterMeshRenderer.enabled = false;
skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender; skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender;
skeletonRenderer.GenerateMeshOverride += SeparateSkeletonRender; skeletonRenderer.GenerateMeshOverride += SeparateSkeletonRender;
@ -46,55 +48,85 @@ namespace Spine.Unity {
void OnDisable () { void OnDisable () {
if (skeletonRenderer == null) return; if (skeletonRenderer == null) return;
if (controlMainMeshRenderer)
masterMeshRenderer.enabled = true;
skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender; skeletonRenderer.GenerateMeshOverride -= SeparateSkeletonRender;
foreach (var s in renderers) #if UNITY_EDITOR
skeletonRenderer.LateUpdate();
#endif
foreach (var s in partsRenderers)
s.ClearMesh(); s.ClearMesh();
} }
MaterialPropertyBlock block; MaterialPropertyBlock block;
void SeparateSkeletonRender (SkeletonRenderer.SmartMesh.Instruction instruction) { void SeparateSkeletonRender (SkeletonRenderer.SmartMesh.Instruction instruction) {
int rendererCount = renderers.Count; int rendererCount = partsRenderers.Count;
if (rendererCount <= 0) return; if (rendererCount <= 0) return;
int rendererIndex = 0; int rendererIndex = 0;
if (propagateMaterialPropertyBlock) if (copyPropertyBlock)
masterMeshRenderer.GetPropertyBlock(block); mainMeshRenderer.GetPropertyBlock(block);
var submeshInstructions = instruction.submeshInstructions; var submeshInstructions = instruction.submeshInstructions;
var submeshInstructionsItems = submeshInstructions.Items; var submeshInstructionsItems = submeshInstructions.Items;
int lastSubmeshInstruction = submeshInstructions.Count - 1; int lastSubmeshInstruction = submeshInstructions.Count - 1;
var currentRenderer = renderers[rendererIndex]; var currentRenderer = partsRenderers[rendererIndex];
bool skeletonRendererCalculateNormals = skeletonRenderer.calculateNormals;
bool useLightProbes = false;
bool receiveShadows = false;
if (copyMeshRendererFlags) {
useLightProbes = mainMeshRenderer.useLightProbes;
receiveShadows = mainMeshRenderer.receiveShadows;
}
for (int i = 0, start = 0; i <= lastSubmeshInstruction; i++) { for (int i = 0, start = 0; i <= lastSubmeshInstruction; i++) {
if (submeshInstructionsItems[i].separatedBySlot) { if (submeshInstructionsItems[i].separatedBySlot) {
//Debug.Log(submeshInstructionsItems[i].endSlot); currentRenderer.RenderParts(instruction.submeshInstructions, start, i + 1);
currentRenderer.RenderSubmesh(instruction.submeshInstructions, start, i + 1); currentRenderer.MeshGenerator.GenerateNormals = skeletonRendererCalculateNormals;
start = i + 1; if (copyMeshRendererFlags) {
var mr = currentRenderer.MeshRenderer;
if (propagateMaterialPropertyBlock) mr.useLightProbes = useLightProbes;
mr.receiveShadows = receiveShadows;
}
if (copyPropertyBlock)
currentRenderer.SetPropertyBlock(block); currentRenderer.SetPropertyBlock(block);
start = i + 1;
rendererIndex++; rendererIndex++;
if (rendererIndex < rendererCount) { if (rendererIndex < rendererCount) {
currentRenderer = renderers[rendererIndex]; currentRenderer = partsRenderers[rendererIndex];
} else { } else {
// Not enough renderers. Skip the rest of the instructions.
break; break;
} }
} else if (i == lastSubmeshInstruction) { } else if (i == lastSubmeshInstruction) {
//Debug.Log(submeshInstructionsItems[i].endSlot); currentRenderer.RenderParts(instruction.submeshInstructions, start, i + 1);
currentRenderer.RenderSubmesh(instruction.submeshInstructions, start, i + 1); currentRenderer.MeshGenerator.GenerateNormals = skeletonRendererCalculateNormals;
if (copyMeshRendererFlags) {
if (propagateMaterialPropertyBlock) var mr = currentRenderer.MeshRenderer;
mr.useLightProbes = useLightProbes;
mr.receiveShadows = receiveShadows;
}
if (copyPropertyBlock)
currentRenderer.SetPropertyBlock(block); currentRenderer.SetPropertyBlock(block);
rendererIndex++;
} }
} }
// Too many renderers. Clear the rest.
if (rendererIndex < rendererCount - 1) {
for (int i = rendererIndex; i < rendererCount; i++) {
currentRenderer = partsRenderers[i];
currentRenderer.ClearMesh();
}
}
} }

View File

@ -30,6 +30,7 @@
*****************************************************************************/ *****************************************************************************/
#define SPINE_OPTIONAL_NORMALS #define SPINE_OPTIONAL_NORMALS
#define SPINE_OPTIONAL_FRONTFACING #define SPINE_OPTIONAL_FRONTFACING
#define SPINE_OPTIONAL_RENDEROVERRIDE
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -48,29 +49,51 @@ public class SkeletonRenderer : MonoBehaviour {
public SkeletonDataAsset skeletonDataAsset; public SkeletonDataAsset skeletonDataAsset;
public String initialSkinName; public String initialSkinName;
#region Advanced #region Advanced
#if SPINE_OPTIONAL_NORMALS #if SPINE_OPTIONAL_NORMALS
public bool calculateNormals, calculateTangents; public bool calculateNormals, calculateTangents;
#endif #endif
public float zSpacing; public float zSpacing;
public bool renderMeshes = true, immutableTriangles; public bool renderMeshes = true, immutableTriangles;
public bool pmaVertexColors = true;
#if SPINE_OPTIONAL_FRONTFACING #if SPINE_OPTIONAL_FRONTFACING
public bool frontFacing; public bool frontFacing;
#endif #endif
#if SPINE_OPTIONAL_RENDEROVERRIDE
public bool disableMeshRendererOnOverride = true;
public delegate void InstructionDelegate (SkeletonRenderer.SmartMesh.Instruction instruction);
event InstructionDelegate MeshOverrideDelegate;
public event InstructionDelegate GenerateMeshOverride {
add {
MeshOverrideDelegate += value;
if (disableMeshRendererOnOverride && MeshOverrideDelegate != null) {
meshRenderer.enabled = false;
}
}
remove {
MeshOverrideDelegate -= value;
if (disableMeshRendererOnOverride && MeshOverrideDelegate == null) {
meshRenderer.enabled = true;
}
}
}
#endif
public bool logErrors = false; public bool logErrors = false;
// Submesh Separation // Submesh Separation
[SpineSlot] public string[] submeshSeparators = new string[0]; [UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")]
[System.NonSerialized] public List<Slot> submeshSeparatorSlots = new List<Slot>(); [SpineSlot]
public string[] separatorSlotNames = new string[0];
[System.NonSerialized]
public List<Slot> separatorSlots = new List<Slot>();
// Custom Slot Material // Custom Slot Material
[System.NonSerialized] readonly Dictionary<Slot, Material> customSlotMaterials = new Dictionary<Slot, Material>(); [System.NonSerialized] readonly Dictionary<Slot, Material> customSlotMaterials = new Dictionary<Slot, Material>();
public Dictionary<Slot, Material> CustomSlotMaterials { get { return customSlotMaterials; } } public Dictionary<Slot, Material> CustomSlotMaterials { get { return customSlotMaterials; } }
#endregion
// Custom Mesh Generation Override
public delegate void InstructionDelegate (SkeletonRenderer.SmartMesh.Instruction instruction);
public event InstructionDelegate GenerateMeshOverride;
#endregion
[System.NonSerialized] public bool valid; [System.NonSerialized] public bool valid;
[System.NonSerialized] public Skeleton skeleton; [System.NonSerialized] public Skeleton skeleton;
@ -160,9 +183,9 @@ public class SkeletonRenderer : MonoBehaviour {
if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default") if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default")
skeleton.SetSkin(initialSkinName); skeleton.SetSkin(initialSkinName);
submeshSeparatorSlots.Clear(); separatorSlots.Clear();
for (int i = 0; i < submeshSeparators.Length; i++) { for (int i = 0; i < separatorSlotNames.Length; i++) {
submeshSeparatorSlots.Add(skeleton.FindSlot(submeshSeparators[i])); separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i]));
} }
LateUpdate(); LateUpdate();
@ -175,7 +198,14 @@ public class SkeletonRenderer : MonoBehaviour {
if (!valid) if (!valid)
return; return;
if (!meshRenderer.enabled && GenerateMeshOverride == null) if (
!meshRenderer.enabled
#if SPINE_OPTIONAL_RENDEROVERRIDE
&& MeshOverrideDelegate == null
#endif
)
return; return;
// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes.
@ -185,7 +215,7 @@ public class SkeletonRenderer : MonoBehaviour {
ExposedList<Slot> drawOrder = skeleton.drawOrder; ExposedList<Slot> drawOrder = skeleton.drawOrder;
var drawOrderItems = drawOrder.Items; var drawOrderItems = drawOrder.Items;
int drawOrderCount = drawOrder.Count; int drawOrderCount = drawOrder.Count;
int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count; int separatorSlotCount = separatorSlots.Count;
bool renderMeshes = this.renderMeshes; bool renderMeshes = this.renderMeshes;
// Clear last state of attachments and submeshes // Clear last state of attachments and submeshes
@ -267,7 +297,7 @@ 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)
bool separatedBySlot = (submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot)); bool separatedBySlot = (separatorSlotCount > 0 && separatorSlots.Contains(slot));
if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) || separatedBySlot) { if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) || separatedBySlot) {
workingSubmeshInstructions.Add( workingSubmeshInstructions.Add(
new Spine.Unity.MeshGeneration.SubmeshInstruction { new Spine.Unity.MeshGeneration.SubmeshInstruction {
@ -313,10 +343,11 @@ public class SkeletonRenderer : MonoBehaviour {
workingInstruction.frontFacing = this.frontFacing; workingInstruction.frontFacing = this.frontFacing;
#endif #endif
if (GenerateMeshOverride != null) { #if SPINE_OPTIONAL_RENDEROVERRIDE
GenerateMeshOverride(workingInstruction); if (MeshOverrideDelegate != null) {
return; MeshOverrideDelegate(workingInstruction);
} }
#endif
// STEP 2. Update vertex buffer based on verts from the attachments. // STEP 2. Update vertex buffer based on verts from the attachments.
// Uses values that were also stored in workingInstruction. // Uses values that were also stored in workingInstruction.
@ -334,13 +365,6 @@ public class SkeletonRenderer : MonoBehaviour {
Vector3 normal = new Vector3(0, 0, -1); Vector3 normal = new Vector3(0, 0, -1);
for (int i = 0; i < vertexCount; i++) for (int i = 0; i < vertexCount; i++)
localNormals[i] = normal; localNormals[i] = normal;
if (calculateTangents) {
Vector4[] localTangents = this.tangents = new Vector4[vertexCount];
Vector4 tangent = new Vector4(1, 0, 0, -1);
for (int i = 0; i < vertexCount; i++)
localTangents[i] = tangent;
}
} }
#endif #endif
} else { } else {
@ -354,6 +378,7 @@ public class SkeletonRenderer : MonoBehaviour {
Vector2[] uvs = this.uvs; Vector2[] uvs = this.uvs;
Color32[] colors = this.colors; Color32[] colors = this.colors;
int vertexIndex = 0; int vertexIndex = 0;
bool pmaVertexColors = this.pmaVertexColors;
Color32 color; Color32 color;
float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b; float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
@ -400,11 +425,19 @@ public class SkeletonRenderer : MonoBehaviour {
vertices[vertexIndex + 3].y = y3; vertices[vertexIndex + 3].y = y3;
vertices[vertexIndex + 3].z = z; vertices[vertexIndex + 3].z = z;
color.a = (byte)(a * slot.a * regionAttachment.a); if (pmaVertexColors) {
color.r = (byte)(r * slot.r * regionAttachment.r * color.a); color.a = (byte)(a * slot.a * regionAttachment.a);
color.g = (byte)(g * slot.g * regionAttachment.g * color.a); color.r = (byte)(r * slot.r * regionAttachment.r * color.a);
color.b = (byte)(b * slot.b * regionAttachment.b * color.a); color.g = (byte)(g * slot.g * regionAttachment.g * color.a);
if (slot.data.blendMode == BlendMode.additive) color.a = 0; color.b = (byte)(b * slot.b * regionAttachment.b * color.a);
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
} else {
color.a = (byte)(a * slot.a * regionAttachment.a);
color.r = (byte)(r * slot.r * regionAttachment.r * 255);
color.g = (byte)(g * slot.g * regionAttachment.g * 255);
color.b = (byte)(b * slot.b * regionAttachment.b * 255);
}
colors[vertexIndex] = color; colors[vertexIndex] = color;
colors[vertexIndex + 1] = color; colors[vertexIndex + 1] = color;
colors[vertexIndex + 2] = color; colors[vertexIndex + 2] = color;
@ -467,11 +500,18 @@ public class SkeletonRenderer : MonoBehaviour {
this.tempVertices = tempVertices = new float[meshVertexCount]; this.tempVertices = tempVertices = new float[meshVertexCount];
meshAttachment.ComputeWorldVertices(slot, tempVertices); meshAttachment.ComputeWorldVertices(slot, tempVertices);
color.a = (byte)(a * slot.a * meshAttachment.a); if (pmaVertexColors) {
color.r = (byte)(r * slot.r * meshAttachment.r * color.a); color.a = (byte)(a * slot.a * meshAttachment.a);
color.g = (byte)(g * slot.g * meshAttachment.g * color.a); color.r = (byte)(r * slot.r * meshAttachment.r * color.a);
color.b = (byte)(b * slot.b * meshAttachment.b * color.a); color.g = (byte)(g * slot.g * meshAttachment.g * color.a);
if (slot.data.blendMode == BlendMode.additive) color.a = 0; color.b = (byte)(b * slot.b * meshAttachment.b * color.a);
if (slot.data.blendMode == BlendMode.additive) color.a = 0;
} else {
color.a = (byte)(a * slot.a * meshAttachment.a);
color.r = (byte)(r * slot.r * meshAttachment.r * 255);
color.g = (byte)(g * slot.g * meshAttachment.g * 255);
color.b = (byte)(b * slot.b * meshAttachment.b * 255);
}
float[] meshUVs = meshAttachment.uvs; float[] meshUVs = meshAttachment.uvs;
float z = i * zSpacing; float z = i * zSpacing;
@ -501,11 +541,18 @@ public class SkeletonRenderer : MonoBehaviour {
this.tempVertices = tempVertices = new float[meshVertexCount]; this.tempVertices = tempVertices = new float[meshVertexCount];
weightedMeshAttachment.ComputeWorldVertices(slot, tempVertices); weightedMeshAttachment.ComputeWorldVertices(slot, tempVertices);
color.a = (byte)(a * slot.a * weightedMeshAttachment.a); if (pmaVertexColors) {
color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a); color.a = (byte)(a * slot.a * weightedMeshAttachment.a);
color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a); color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a);
color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a); color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a);
if (slot.data.blendMode == BlendMode.additive) color.a = 0; 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[] meshUVs = weightedMeshAttachment.uvs; float[] meshUVs = weightedMeshAttachment.uvs;
float z = i * zSpacing; float z = i * zSpacing;
@ -545,9 +592,19 @@ public class SkeletonRenderer : MonoBehaviour {
if (currentSmartMeshInstructionUsed.vertexCount < vertexCount) { if (currentSmartMeshInstructionUsed.vertexCount < vertexCount) {
if (calculateNormals) { if (calculateNormals) {
currentMesh.normals = normals; currentMesh.normals = normals;
if (calculateTangents) { }
currentMesh.tangents = tangents;
// For dynamic calculated tangents, this needs to be moved out of the if block when replacing the logic.
if (calculateTangents) {
if (tangents == null || vertexCount > tangents.Length) {
Vector4[] localTangents = this.tangents = new Vector4[vertexCount];
Vector4 tangent = new Vector4(1, 0, 0, -1);
for (int i = 0; i < vertexCount; i++)
localTangents[i] = tangent;
} }
currentMesh.tangents = this.tangents;
} }
} }
#endif #endif
@ -893,6 +950,8 @@ public class SkeletonRenderer : MonoBehaviour {
class SubmeshTriangleBuffer { class SubmeshTriangleBuffer {
public int[] triangles = new int[0]; public int[] triangles = new int[0];
// These two fields are used when renderMeshes == false
public int triangleCount; public int triangleCount;
public int firstVertex = -1; public int firstVertex = -1;
} }