@Nate that would be awesome π
I think that the shadows showing through the player when flipX is set to true isn't a shader issue but instead is because the mesh generated isn't double sided. @Anyone how would I go about making it double sided?
I found this script in the forums:
var mesh = GetComponent(MeshFilter).mesh;
var vertices = mesh.vertices;
var uv = mesh.uv;
var normals = mesh.normals;
var szV = vertices.length;
var newVerts = new Vector3[szV*2];
var newUv = new Vector2[szV*2];
var newNorms = new Vector3[szV*2];
for (var j=0; j< szV; j++){
// duplicate vertices and uvs:
newVerts[j] = newVerts[j+szV] = vertices[j];
newUv[j] = newUv[j+szV] = uv[j];
// copy the original normals...
newNorms[j] = normals[j];
// and revert the new ones
newNorms[j+szV] = -normals[j];
}
var triangles = mesh.triangles;
var szT = triangles.length;
var newTris = new int[szT*2]; // double the triangles
for (var i=0; i< szT; i+=3){
// copy the original triangle
newTris[i] = triangles[i];
newTris[i+1] = triangles[i+1];
newTris[i+2] = triangles[i+2];
// save the new reversed triangle
j = i+szT;
newTris[j] = triangles[i]+szV;
newTris[j+2] = triangles[i+1]+szV;
newTris[j+1] = triangles[i+2]+szV;
}
mesh.vertices = newVerts;
mesh.uv = newUv;
mesh.normals = newNorms;
mesh.triangles = newTris; // assign triangles last!
I translated it to c#, placed it where I thought it would be most likely to work aaaaaand failure. The vertices increase really really fast and my editor locks up. Feel much retarded.
My SkeletonComponent script as is (the function of interest being WindTriangles()):
/*******************************************************************************
* 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 int lastVertexCount;
private Vector3[] vertices;
private Color32[] 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;
mesh.name = this.gameObject.name;
mesh.hideFlags = HideFlags.HideAndDontSave;
mesh.MarkDynamic();
renderer.sharedMaterial = skeletonDataAsset.atlasAsset.material;
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.
Vector3[] vertices = this.vertices;
int vertexCount = quadCount * 8;
bool newTriangles = vertexCount > vertices.Length;
if (newTriangles) {
// Not enough vertices, increase size.
this.vertices = vertices = new Vector3[vertexCount];
colors = new Color32[vertexCount];
uvs = new Vector2[vertexCount];
triangles = new int[quadCount * 6];
mesh.Clear();
for (int i = 0, n = quadCount; i < n; i++) {
int index = i * 6;
int vertex = i * 4;
triangles[index] = vertex;
triangles[index + 1] = vertex + 2;
triangles[index + 2] = vertex + 1;
triangles[index + 3] = vertex + 2;
triangles[index + 4] = vertex + 3;
triangles[index + 5] = vertex + 1;
}
} else {
// Too many vertices, zero the extra.
Vector3 zero = new Vector3(0, 0, 0);
for (int i = vertexCount, n = lastVertexCount; i < n; i++)
vertices[i] = zero;
}
lastVertexCount = vertexCount;
// Setup mesh.
float[] vertexPositions = this.vertexPositions;
int vertexIndex = 0;
Color32 color = new Color32();
for (int i = 0, n = drawOrder.Count; i < n; i++) {
Slot slot = drawOrder[i];
RegionAttachment regionAttachment = slot.Attachment as RegionAttachment;
if (regionAttachment == null) continue;
regionAttachment.ComputeVertices(skeleton.X, skeleton.Y, slot.Bone, vertexPositions);
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 = (byte)(skeleton.A * slot.A * 255);
color.r = (byte)(skeleton.R * slot.R * color.a);
color.g = (byte)(skeleton.G * slot.G * color.a);
color.b = (byte)(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]);
vertexIndex += 4;
}
mesh.vertices = vertices;
mesh.colors32 = colors;
mesh.uv = uvs;
if (newTriangles) mesh.triangles = triangles;
if (skeleton.FlipX)
{
WindTriangles(mesh);
}
mesh.RecalculateNormals();
ComputeTangents(mesh);
}
private void WindTriangles(Mesh SkeletonMesh) {
Vector3[] _vertices = SkeletonMesh.vertices;
Vector2[] _uv = SkeletonMesh.uv;
Vector3[] _normals = SkeletonMesh.normals;
int szV = _vertices.Length;
Vector3[] newVerts = new Vector3[szV*2];
Vector2[] newUv = new Vector2[szV*2];
Vector3[] newNorms = new Vector3[szV*2];
for (var j=0; j< szV; j++){
// duplicate vertices and uvs:
newVerts[j] = newVerts[j+szV] = _vertices[j];
newUv[j] = newUv[j+szV] = _uv[j];
// copy the original normals...
newNorms[j] = _normals[j];
// and revert the new ones
newNorms[j+szV] = -_normals[j];
}
int[] _triangles = SkeletonMesh.triangles;
int szT = _triangles.Length;
int[] newTris = new int[szT*2]; // double the triangles
for (var i=0; i< szT; i+=3){
// copy the original triangle
newTris[i] = _triangles[i];
newTris[i+1] = _triangles[i+1];
newTris[i+2] = _triangles[i+2];
// save the new reversed triangle
int j = i+szT;
newTris[j] = _triangles[i]+szV;
newTris[j+2] = _triangles[i+1]+szV;
newTris[j+1] = _triangles[i+2]+szV;
}
SkeletonMesh.vertices = newVerts;
SkeletonMesh.uv = newUv;
SkeletonMesh.normals = newNorms;
SkeletonMesh.triangles = newTris; // assign triangles last!
}
private 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;
}
public virtual void OnEnable () {
Update();
}
public virtual void OnDisable () {
if (Application.isEditor)
Clear();
}
public virtual void Reset () {
Update();
}
#region Unity Editor
#if UNITY_EDITOR
void OnDrawGizmos() {
Vector3 gizmosCenter = new Vector3();
Vector3 gizmosSize = new Vector3();
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, 0f);
Vector3 max = new Vector3(float.MinValue, float.MinValue, 0f);
foreach (Vector3 vert in vertices) {
min = Vector3.Min (min, vert);
max = Vector3.Max (max, vert);
}
float width = max.x - min.x;
float height = max.y - min.y;
gizmosCenter = new Vector3(min.x + (width / 2f), min.y + (height / 2f), 0f);
gizmosSize = new Vector3(width, height, 1f);
Gizmos.color = Color.clear;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawCube(gizmosCenter, gizmosSize);
}
#endif
#endregion
}