Created
September 15, 2019 13:31
-
-
Save runevision/f081c6e322a7da4152e40c9a5bddeb05 to your computer and use it in GitHub Desktop.
Redistribute UVs PostProcessor for Unity
This file contains hidden or 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
// This can be used to redistribute the UV coordinates | |
// (either, U, V, or both) on an imported mesh which represents an | |
// extruded surface, such as ropes, or other shapes extruded along a | |
// spline. It's important for this method to work, that all vertices | |
// of each edge loop have the same U or V coordinate (depending on | |
// which is chosen for redistribution). | |
// | |
// Usage: | |
// | |
// Meshes that should be redistributed should contain "Redistribute" | |
// in their name, followed by "U", "V" or both. | |
// Example: "MyMesh_RedistributeU". | |
// | |
// The name may also contain "Flip", in which case U and V are flipped. | |
// This is done prior to redistributing. | |
// (This will flip mesh tangents and binormals as well so the tangents | |
// are aligned with the new U directions in the mesh.) | |
// Example: "MyMesh_Flip_RedistributeV". | |
// | |
// The model should have a label "RedistributeUVs" added in Unity. | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
using UnityEditor; | |
public class RedistributeUVsPostProcessor : AssetPostprocessor { | |
void OnPostprocessModel (GameObject g) { | |
var labels = AssetDatabase.GetLabels (assetImporter); | |
bool found = false; | |
foreach (var label in labels) { | |
Debug.Log ("Label: "+label); | |
if (label == "RedistributeUVs") { | |
found = true; | |
break; | |
} | |
} | |
if (!found) | |
return; | |
Apply(g.transform); | |
} | |
void Apply (Transform t) { | |
var invariant = System.StringComparison.InvariantCulture; | |
MeshFilter filter = t.GetComponent<MeshFilter> (); | |
if (filter != null && filter.sharedMesh != null) { | |
if (filter.sharedMesh.name.Contains ("Flip")) | |
FlipUVs (filter.sharedMesh); | |
int index = filter.sharedMesh.name.IndexOf ("Redistribute", invariant); | |
if (index >= 0) { | |
if (filter.sharedMesh.name.IndexOf ("U", index + 12, invariant) >= 0) | |
Redistribute (filter.sharedMesh, 0); | |
if (filter.sharedMesh.name.IndexOf ("V", index + 12, invariant) >= 0) | |
Redistribute (filter.sharedMesh, 1); | |
if (filter.sharedMesh.name.IndexOf ("Flip", index + 12, invariant) >= 0) | |
Redistribute (filter.sharedMesh, 1); | |
} | |
} | |
// Recurse | |
foreach (Transform child in t) | |
Apply(child); | |
} | |
void FlipUVs (Mesh mesh) { | |
Vector2[] uvs = mesh.uv; | |
Vector3[] normals = mesh.normals; | |
Vector4[] tangents = mesh.tangents; | |
for (int i = uvs.Length - 1; i >= 0; i--) { | |
uvs[i] = new Vector2 (uvs[i].y, uvs[i].x); | |
Vector3 tangent = (Vector3)tangents[i]; | |
Vector3 binormal = Vector3.Cross (normals[i], tangent) * tangents[i].w; | |
tangents[i] = new Vector4 (binormal.x, binormal.y, binormal.z, -tangents[i].w); | |
} | |
mesh.uv = uvs; | |
mesh.tangents = tangents; | |
} | |
const int tolerance = 100000; | |
class VertexGroup { | |
public readonly float texCoord; | |
List<Vector3> vertices = new List<Vector3> (); | |
public Vector3 avgPos; | |
public float newTexCoord; | |
public VertexGroup (float texCoord) { | |
this.texCoord = texCoord; | |
} | |
public void AddVertex (Vector3 vertex) { | |
avgPos += vertex; | |
vertices.Add (vertex); | |
} | |
public void CalcAvgPos () { | |
avgPos /= vertices.Count; | |
} | |
} | |
void Redistribute (Mesh mesh, int uvAxis) { | |
Dictionary<int, VertexGroup> groupDict = new Dictionary<int, VertexGroup> (); | |
Vector3[] vertices = mesh.vertices; | |
Vector2[] uvs = mesh.uv; | |
// Create vertex groups and add vertices to them. | |
for (int i = vertices.Length - 1; i >= 0; i--) { | |
Vector3 vertex = vertices[i]; | |
Vector2 uv = uvs[i]; | |
float texCoord = uv[uvAxis]; | |
int key = Mathf.RoundToInt (texCoord * tolerance); | |
VertexGroup group; | |
if (!groupDict.TryGetValue (key, out group)) { | |
group = new VertexGroup (texCoord); | |
groupDict[key] = group; | |
} | |
group.AddVertex (vertex); | |
} | |
Debug.Log ("Created " + groupDict.Count + " vertex groups from " + vertices.Length + " vertices."); | |
// Sort vertex groups. | |
List<VertexGroup> groupList = groupDict.Values.OrderBy (e => e.texCoord).ToList (); | |
// Calculate average positions and cumulative lengths for vertex groups. | |
float cumulativeLength = 0; | |
groupList[0].CalcAvgPos (); | |
for (int i = 1; i < groupList.Count; i++) { | |
groupList[i].CalcAvgPos (); | |
float length = Vector3.Distance (groupList[i].avgPos, groupList[i - 1].avgPos); | |
cumulativeLength += length; | |
groupList[i].newTexCoord = cumulativeLength; | |
} | |
// Redistribute texCoord based on lengths but in same range as originally. | |
float lowestTexCoord = groupList.First ().texCoord; | |
float highestTexCoord = groupList.Last ().texCoord; | |
for (int i = 0; i < groupList.Count; i++) { | |
groupList[i].newTexCoord = Mathf.LerpUnclamped ( | |
lowestTexCoord, | |
highestTexCoord, | |
groupList[i].newTexCoord / cumulativeLength); | |
} | |
// Assign texcoords back to mesh | |
for (int i = uvs.Length - 1; i >= 0; i--) { | |
Vector2 uv = uvs[i]; | |
float texCoord = uv[uvAxis]; | |
int key = Mathf.RoundToInt (texCoord * tolerance); | |
VertexGroup group = groupDict[key]; | |
uv[uvAxis] = group.newTexCoord; | |
uvs[i] = uv; | |
} | |
mesh.uv = uvs; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment