tl;dr Almost working, issues with z-fighting, flipping and transparency. Code at bottom of post
Attempt 2:
Research revealed that the reason I was getting an error was because tangents weren't being calculated for the mesh (mesh.tangents was equal to null). So, I set about looking for a way to do just that. This required me to change the SkeletonComponent.cs file.
Changes:
- @ Line 58
mesh.name = this.gameObject.name; // Instead of naming every mesh "Skeletal Mesh"
- @ Line 164 & 165
mesh.RecalculateNormals();
ComputeTangents(mesh);
ComputeTangents is a function I copy pasta'd from this here discussion.
The result:
Loading Image
It responds to moving lights. Still has z fighting issues and it... becomes transparent when near a light source. Kind of like if it had see through skin?
Modified SkeletonComponent.cs file:
/*******************************************************************************
* Copyright (c) 2013, Esoteric Software
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.IO;
using System.Collections.Generic;
using UnityEngine;
using Spine;
/** Renders a skeleton. Extend to apply animations, get bones and manipulate them, etc. */
[ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class SkeletonComponent : MonoBehaviour {
public SkeletonDataAsset skeletonDataAsset;
public Skeleton skeleton;
public String initialSkinName;
public float timeScale = 1;
private Mesh mesh;
private Vector3[] vertices;
private Color[] colors;
private Vector2[] uvs;
private int[] triangles;
private float[] vertexPositions = new float[8];
public virtual void Clear () {
GetComponent<MeshFilter>().mesh = null;
DestroyImmediate(mesh);
mesh = null;
renderer.sharedMaterial = null;
skeleton = null;
}
public virtual void Initialize () {
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
// Instead of naming every mesh "Skeletal Mesh"
mesh.name = this.gameObject.name;
mesh.hideFlags = HideFlags.HideAndDontSave;
vertices = new Vector3[0];
skeleton = new Skeleton(skeletonDataAsset.GetSkeletonData(false));
if (initialSkinName != null && initialSkinName.Length > 0) {
skeleton.SetSkin(initialSkinName);
skeleton.SetSlotsToSetupPose();
}
}
public virtual void UpdateSkeleton () {
skeleton.Update(Time.deltaTime * timeScale);
skeleton.UpdateWorldTransform();
}
public virtual void Update () {
// Clear fields if missing information to render.
if (skeletonDataAsset == null || skeletonDataAsset.GetSkeletonData(false) == null) {
Clear();
return;
}
// Initialize fields.
if (skeleton == null || skeleton.Data != skeletonDataAsset.GetSkeletonData(false))
Initialize();
UpdateSkeleton();
// Count quads.
int quadCount = 0;
List<Slot> drawOrder = skeleton.DrawOrder;
for (int i = 0, n = drawOrder.Count; i < n; i++) {
Slot slot = drawOrder[i];
Attachment attachment = slot.Attachment;
if (attachment is RegionAttachment)
quadCount++;
}
// Ensure mesh data is the right size.
if (quadCount > vertices.Length / 4) {
vertices = new Vector3[quadCount * 4];
colors = new Color[quadCount * 4];
uvs = new Vector2[quadCount * 4];
triangles = new int[quadCount * 6];
mesh.Clear();
for (int i = 0, n = quadCount; i < n; i++) {
int index = i * 6;
int vertexIndex = i * 4;
triangles[index] = vertexIndex;
triangles[index + 1] = vertexIndex + 2;
triangles[index + 2] = vertexIndex + 1;
triangles[index + 3] = vertexIndex + 2;
triangles[index + 4] = vertexIndex + 3;
triangles[index + 5] = vertexIndex + 1;
}
} else {
Vector3 zero = new Vector3(0, 0, 0);
for (int i = quadCount * 4, n = vertices.Length; i < n; i++)
vertices[i] = zero;
}
// Setup mesh.
float[] vertexPositions = this.vertexPositions;
int quadIndex = 0;
Color color = new Color();
for (int i = 0, n = drawOrder.Count; i < n; i++) {
Slot slot = drawOrder[i];
Attachment attachment = slot.Attachment;
if (attachment is RegionAttachment) {
RegionAttachment regionAttachment = (RegionAttachment)attachment;
regionAttachment.ComputeVertices(skeleton.X, skeleton.Y, slot.Bone, vertexPositions);
int vertexIndex = quadIndex * 4;
vertices[vertexIndex] = new Vector3(vertexPositions[RegionAttachment.X1], vertexPositions[RegionAttachment.Y1], 0);
vertices[vertexIndex + 1] = new Vector3(vertexPositions[RegionAttachment.X4], vertexPositions[RegionAttachment.Y4], 0);
vertices[vertexIndex + 2] = new Vector3(vertexPositions[RegionAttachment.X2], vertexPositions[RegionAttachment.Y2], 0);
vertices[vertexIndex + 3] = new Vector3(vertexPositions[RegionAttachment.X3], vertexPositions[RegionAttachment.Y3], 0);
color.a = skeleton.A * slot.A;
color.r = skeleton.R * slot.R * color.a;
color.g = skeleton.G * slot.G * color.a;
color.b = skeleton.B * slot.B * color.a;
colors[vertexIndex] = color;
colors[vertexIndex + 1] = color;
colors[vertexIndex + 2] = color;
colors[vertexIndex + 3] = color;
float[] regionUVs = regionAttachment.UVs;
uvs[vertexIndex] = new Vector2(regionUVs[RegionAttachment.X1], 1 - regionUVs[RegionAttachment.Y1]);
uvs[vertexIndex + 1] = new Vector2(regionUVs[RegionAttachment.X4], 1 - regionUVs[RegionAttachment.Y4]);
uvs[vertexIndex + 2] = new Vector2(regionUVs[RegionAttachment.X2], 1 - regionUVs[RegionAttachment.Y2]);
uvs[vertexIndex + 3] = new Vector2(regionUVs[RegionAttachment.X3], 1 - regionUVs[RegionAttachment.Y3]);
quadIndex++;
}
}
mesh.vertices = vertices;
mesh.colors = colors;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.RecalculateNormals();
ComputeTangents(mesh);
renderer.sharedMaterial = skeletonDataAsset.atlasAsset.material;
}
public virtual void OnEnable () {
Update();
}
public virtual void OnDisable () {
if (Application.isEditor)
Clear();
}
public virtual void Reset () {
Update();
}
void ComputeTangents(Mesh mesh)
{
int triangleCount = mesh.triangles.Length;
int vertexCount = mesh.vertices.Length;
Vector3[] tan1 = new Vector3[vertexCount];
Vector3[] tan2 = new Vector3[vertexCount];
Vector4[] tangents = new Vector4[vertexCount];
for(long a = 0; a < triangleCount; a+=3)
{
long i1 = mesh.triangles[a+0];
long i2 = mesh.triangles[a+1];
long i3 = mesh.triangles[a+2];
Vector3 v1 = mesh.vertices[i1];
Vector3 v2 = mesh.vertices[i2];
Vector3 v3 = mesh.vertices[i3];
Vector2 w1 = mesh.uv[i1];
Vector2 w2 = mesh.uv[i2];
Vector2 w3 = mesh.uv[i3];
float x1 = v2.x - v1.x;
float x2 = v3.x - v1.x;
float y1 = v2.y - v1.y;
float y2 = v3.y - v1.y;
float z1 = v2.z - v1.z;
float z2 = v3.z - v1.z;
float s1 = w2.x - w1.x;
float s2 = w3.x - w1.x;
float t1 = w2.y - w1.y;
float t2 = w3.y - w1.y;
float r = 1.0f / (s1 * t2 - s2 * t1);
Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
tan1[i1] += sdir;
tan1[i2] += sdir;
tan1[i3] += sdir;
tan2[i1] += tdir;
tan2[i2] += tdir;
tan2[i3] += tdir;
}
for (long a = 0; a < vertexCount; ++a)
{
Vector3 n = mesh.normals[a];
Vector3 t = tan1[a];
Vector3 tmp = (t - n * Vector3.Dot(n, t)).normalized;
tangents[a] = new Vector4(tmp.x, tmp.y, tmp.z);
tangents[a].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
}
mesh.tangents = tangents;
}
}