400 lines
14 KiB
C#
400 lines
14 KiB
C#
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using UnityEngine;
|
|||
|
|
|||
|
[RequireComponent(typeof(Cloth))]
|
|||
|
//[ExecuteInEditMode]
|
|||
|
public class ClothTools : MonoBehaviour
|
|||
|
{
|
|||
|
[SerializeField, HideInInspector]
|
|||
|
public SkinnedMeshRenderer m_Skinned;
|
|||
|
[SerializeField, HideInInspector]
|
|||
|
public Cloth m_Cloth;
|
|||
|
|
|||
|
[System.Serializable]
|
|||
|
class SubmeshMeta
|
|||
|
{
|
|||
|
public int submesh_index;
|
|||
|
public string submesh_name;
|
|||
|
public int vertex_offset;
|
|||
|
public int vertex_count;
|
|||
|
|
|||
|
public SubmeshMeta(int index, string name, int offset, int count)
|
|||
|
{
|
|||
|
submesh_index = index;
|
|||
|
submesh_name = name;
|
|||
|
vertex_offset = offset;
|
|||
|
vertex_count = count;
|
|||
|
}
|
|||
|
|
|||
|
public int Start()
|
|||
|
{
|
|||
|
return vertex_offset;
|
|||
|
}
|
|||
|
|
|||
|
public int Stop()
|
|||
|
{
|
|||
|
return vertex_offset + vertex_count + 1;
|
|||
|
}
|
|||
|
|
|||
|
public bool InSubmesh(int a_index)
|
|||
|
{
|
|||
|
if ( a_index >= vertex_offset && a_index <= (vertex_offset+vertex_count) )
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
[SerializeField, HideInInspector]
|
|||
|
private List<SubmeshMeta> m_SubmeshMeta;
|
|||
|
|
|||
|
[SerializeField, HideInInspector]
|
|||
|
private CollapsedVertexArray m_CollapsedVerts;
|
|||
|
|
|||
|
[HideInInspector]
|
|||
|
public TextAsset m_BinaryData;
|
|||
|
[HideInInspector]
|
|||
|
public TextAsset m_TestVertData;
|
|||
|
|
|||
|
void Reset()
|
|||
|
{
|
|||
|
m_Skinned = GetComponent<SkinnedMeshRenderer>();
|
|||
|
m_Cloth = GetComponent<Cloth>();
|
|||
|
m_CollapsedVerts = null;
|
|||
|
GenerateLookupTables();
|
|||
|
}
|
|||
|
|
|||
|
public void GenerateLookupTables()
|
|||
|
{
|
|||
|
m_CollapsedVerts = new CollapsedVertexArray(m_Skinned.sharedMesh.vertices);
|
|||
|
|
|||
|
if (m_Cloth.vertices.Length == m_CollapsedVerts.Length)
|
|||
|
{
|
|||
|
//Debug.Log("collapsed == cloth. Ready for weightmap transfer.");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogError("ClothTools.GenerateLookupTables() ERROR: # collapsed verts (" + m_CollapsedVerts.Length + ") != # cloth verts(" + m_Cloth.vertices.Length + "). Please fix lookup table.");
|
|||
|
}
|
|||
|
|
|||
|
// create submesh meta
|
|||
|
m_SubmeshMeta = new List<SubmeshMeta>();
|
|||
|
for (int i = 0; i < m_Skinned.sharedMesh.subMeshCount; i++)
|
|||
|
{
|
|||
|
var submesh = m_Skinned.sharedMesh.GetSubMesh(i);
|
|||
|
string submesh_name = submesh.ToString();
|
|||
|
if (m_Skinned.sharedMaterials.Length == m_Skinned.sharedMesh.subMeshCount)
|
|||
|
{
|
|||
|
submesh_name = m_Skinned.sharedMaterials[i].name;
|
|||
|
}
|
|||
|
m_SubmeshMeta.Add(new SubmeshMeta(i, submesh_name, submesh.firstVertex, submesh.vertexCount));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public void SetSubMeshWeights(int submesh_index, float weight_value)
|
|||
|
{
|
|||
|
if (m_CollapsedVerts == null || m_CollapsedVerts.Length <= 0)
|
|||
|
{
|
|||
|
//Debug.Log("ClothTools.SetSubMeshWeights(): Lookup tables were reset. Regenerating...");
|
|||
|
GenerateLookupTables();
|
|||
|
}
|
|||
|
|
|||
|
if (submesh_index > m_Skinned.sharedMesh.subMeshCount)
|
|||
|
{
|
|||
|
Debug.LogError("ClothTools.SetSubMeshWeight(): invalid submesh_index=" + submesh_index);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ClothSkinningCoefficient[] newCoefficients = new ClothSkinningCoefficient[m_Cloth.coefficients.Length];
|
|||
|
System.Array.Copy(m_Cloth.coefficients, newCoefficients, newCoefficients.Length);
|
|||
|
|
|||
|
//var submesh = m_Skinned.sharedMesh.GetSubMesh(submesh_index);
|
|||
|
//for (int vertex_index = submesh.firstVertex; vertex_index < (submesh.firstVertex + submesh.vertexCount); vertex_index++)
|
|||
|
//{
|
|||
|
// int cloth_vertex = m_CollapsedVerts.LookupIndex(vertex_index);
|
|||
|
// if (cloth_vertex != -1)
|
|||
|
// newCoefficients[cloth_vertex].maxDistance = weight_value;
|
|||
|
//}
|
|||
|
|
|||
|
var triangle_vertindex_array = m_Skinned.sharedMesh.GetTriangles(submesh_index);
|
|||
|
bool errorOnce = false;
|
|||
|
foreach (int vertex_index in triangle_vertindex_array)
|
|||
|
{
|
|||
|
int cloth_vertex = m_CollapsedVerts.LookupIndex(vertex_index);
|
|||
|
if (cloth_vertex != -1)
|
|||
|
{
|
|||
|
newCoefficients[cloth_vertex].maxDistance = weight_value;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (errorOnce == false)
|
|||
|
{
|
|||
|
errorOnce = true;
|
|||
|
Debug.LogError("ClothTools.SetSubmeshWeights(): submesh[" + submesh_index + "] vertex index lookup did not return valid result for cloth vertex index. ");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
m_Cloth.coefficients = newCoefficients;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void ClearSubMeshWeights(int submesh_index)
|
|||
|
{
|
|||
|
SetSubMeshWeights(submesh_index, float.MaxValue);
|
|||
|
}
|
|||
|
|
|||
|
public void ClearWeightMap()
|
|||
|
{
|
|||
|
|
|||
|
if (m_Cloth == null)
|
|||
|
return;
|
|||
|
|
|||
|
ClothSkinningCoefficient[] newCoefficients = new ClothSkinningCoefficient[m_Cloth.coefficients.Length];
|
|||
|
System.Array.Copy(m_Cloth.coefficients, newCoefficients, newCoefficients.Length);
|
|||
|
|
|||
|
for (int i = 0; i < newCoefficients.Length; i++)
|
|||
|
{
|
|||
|
float maxDistance = newCoefficients[i].maxDistance;
|
|||
|
newCoefficients[i].maxDistance = float.MaxValue;
|
|||
|
}
|
|||
|
|
|||
|
m_Cloth.coefficients = newCoefficients;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void LoadGradientPattern()
|
|||
|
{
|
|||
|
|
|||
|
if (m_Cloth == null)
|
|||
|
return;
|
|||
|
|
|||
|
ClothSkinningCoefficient[] newCoefficients = new ClothSkinningCoefficient[m_Cloth.coefficients.Length];
|
|||
|
System.Array.Copy(m_Cloth.coefficients, newCoefficients, newCoefficients.Length);
|
|||
|
|
|||
|
for (int i = 0; i < newCoefficients.Length; i++)
|
|||
|
{
|
|||
|
float maxDistance = newCoefficients[i].maxDistance;
|
|||
|
// float gradientValue = (float)i / newCoefficients.Length * float.MaxValue;
|
|||
|
float gradientValue = (float)i / newCoefficients.Length;
|
|||
|
newCoefficients[i].maxDistance = gradientValue;
|
|||
|
}
|
|||
|
|
|||
|
m_Cloth.coefficients = newCoefficients;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void LoadSteppedGradient()
|
|||
|
{
|
|||
|
|
|||
|
if (m_Cloth == null)
|
|||
|
return;
|
|||
|
|
|||
|
ClothSkinningCoefficient[] newCoefficients = new ClothSkinningCoefficient[m_Cloth.coefficients.Length];
|
|||
|
System.Array.Copy(m_Cloth.coefficients, newCoefficients, newCoefficients.Length);
|
|||
|
|
|||
|
for (int i = 0; i < newCoefficients.Length; i++)
|
|||
|
{
|
|||
|
float maxDistance = newCoefficients[i].maxDistance;
|
|||
|
float gradientValue = (float)i / newCoefficients.Length;
|
|||
|
int stepping = (int) (gradientValue * 10.0f);
|
|||
|
gradientValue = stepping / 10.0f;
|
|||
|
newCoefficients[i].maxDistance = gradientValue;
|
|||
|
}
|
|||
|
|
|||
|
m_Cloth.coefficients = newCoefficients;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public void SaveWeightMap(string filename)
|
|||
|
{
|
|||
|
ClothSkinningCoefficient[] outCoefficients = new ClothSkinningCoefficient[m_Cloth.coefficients.Length];
|
|||
|
System.Array.Copy(m_Cloth.coefficients, outCoefficients, outCoefficients.Length);
|
|||
|
int numVerts = outCoefficients.Length;
|
|||
|
float[] weights = new float[numVerts];
|
|||
|
int numBytes = sizeof(float) * numVerts;
|
|||
|
byte[] binaryBuffer = new byte[numBytes];
|
|||
|
|
|||
|
for (int i=0; i < numVerts; i++)
|
|||
|
{
|
|||
|
weights[i] = outCoefficients[i].maxDistance;
|
|||
|
}
|
|||
|
|
|||
|
System.Buffer.BlockCopy(weights, 0, binaryBuffer, 0, numBytes);
|
|||
|
System.IO.File.WriteAllBytes(filename, binaryBuffer);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void LoadWeightMap(string filename)
|
|||
|
{
|
|||
|
ClothSkinningCoefficient[] newCoefficients = new ClothSkinningCoefficient[m_Cloth.coefficients.Length];
|
|||
|
System.Array.Copy(m_Cloth.coefficients, newCoefficients, newCoefficients.Length);
|
|||
|
|
|||
|
/////////////////////////////////////////////////////
|
|||
|
/// Load Raw Weight Map
|
|||
|
/////////////////////////////////////////////////////
|
|||
|
int numVerts = newCoefficients.Length;
|
|||
|
float[] weights = new float[numVerts];
|
|||
|
int numBytes = sizeof(float) * numVerts;
|
|||
|
|
|||
|
byte[] binaryBuffer = System.IO.File.ReadAllBytes(filename);
|
|||
|
System.Buffer.BlockCopy(binaryBuffer, 0, weights, 0, numBytes);
|
|||
|
|
|||
|
for (int i=0; i < numVerts; i++)
|
|||
|
{
|
|||
|
newCoefficients[i].maxDistance = weights[i];
|
|||
|
}
|
|||
|
|
|||
|
m_Cloth.coefficients = newCoefficients;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void ImportWeightMap(string filename)
|
|||
|
{
|
|||
|
ClothSkinningCoefficient[] newCoefficients = new ClothSkinningCoefficient[m_Cloth.coefficients.Length];
|
|||
|
System.Array.Copy(m_Cloth.coefficients, newCoefficients, newCoefficients.Length);
|
|||
|
|
|||
|
/////////////////////////////////////////////////////
|
|||
|
/// Load Raw Weight Map
|
|||
|
/////////////////////////////////////////////////////
|
|||
|
int numVerts = newCoefficients.Length ;
|
|||
|
ushort[] weights = new ushort[numVerts];
|
|||
|
int numBytes = sizeof(ushort) * numVerts;
|
|||
|
|
|||
|
byte[] binaryBuffer = System.IO.File.ReadAllBytes(filename);
|
|||
|
System.Buffer.BlockCopy(binaryBuffer, 0, weights, 0, numBytes);
|
|||
|
|
|||
|
float simulation_strength = 0.0f;
|
|||
|
for (int vertex_index = 0; vertex_index < numVerts; vertex_index++)
|
|||
|
{
|
|||
|
int cloth_index = vertex_index;
|
|||
|
if (cloth_index >= newCoefficients.Length)
|
|||
|
{
|
|||
|
Debug.LogError("ClothTools.LoadRawWeightMap(): cloth_index is greater than coefficient array: " + vertex_index + " vs " + newCoefficients.Length);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
simulation_strength = (float) weights[vertex_index] / ushort.MaxValue;
|
|||
|
|
|||
|
//// DEBUG TESTING: set zero value (red) to maxvalue(black)
|
|||
|
//if (simulation_strength == 0) simulation_strength = float.MaxValue;
|
|||
|
|
|||
|
//float strength_max = 1.0f;
|
|||
|
//float strength_min = 0.0f;
|
|||
|
//float strength_scale_threshold = 0.5f;
|
|||
|
float adjusted_simulation_strength = simulation_strength;
|
|||
|
|
|||
|
//// tiered scaling
|
|||
|
//if (simulation_strength <= strength_scale_threshold)
|
|||
|
//{
|
|||
|
// // stronger compression of values below threshold
|
|||
|
// float scale = 0.075f;
|
|||
|
// float offset = 0.2f;
|
|||
|
// adjusted_simulation_strength = (simulation_strength - offset) * scale;
|
|||
|
//}
|
|||
|
//else
|
|||
|
//{
|
|||
|
// float offset = (strength_scale_threshold - 0.2f) * 0.075f; // offset = (threshold - previous tier's offset) * previous teir's scale
|
|||
|
// float scale = 0.2f;
|
|||
|
// adjusted_simulation_strength = (simulation_strength - offset) / (1 - offset); // apply offset, then normalize to 1.0
|
|||
|
// adjusted_simulation_strength *= scale;
|
|||
|
|
|||
|
//}
|
|||
|
//// clamp to 0.0f to 0.2f
|
|||
|
//float coeff_min = 0.0f;
|
|||
|
//float coeff_max = 0.2f;
|
|||
|
//adjusted_simulation_strength = (adjusted_simulation_strength > coeff_min) ? adjusted_simulation_strength : coeff_min;
|
|||
|
//adjusted_simulation_strength = (adjusted_simulation_strength < coeff_max) ? adjusted_simulation_strength : coeff_max;
|
|||
|
|
|||
|
newCoefficients[cloth_index].maxDistance = adjusted_simulation_strength;
|
|||
|
}
|
|||
|
|
|||
|
m_Cloth.coefficients = newCoefficients;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void TestVertData()
|
|||
|
{
|
|||
|
// get verts
|
|||
|
Vector3[] unityVerts = m_Cloth.vertices;
|
|||
|
|
|||
|
int numVerts = m_Cloth.vertices.Length;
|
|||
|
float[] dazVertBuffer = new float[numVerts*3];
|
|||
|
int byte_length = sizeof(float) * numVerts*3;
|
|||
|
Debug.Log("byte_length = " + byte_length);
|
|||
|
System.Buffer.BlockCopy(m_TestVertData.bytes, 0, dazVertBuffer, 0, numVerts*3);
|
|||
|
|
|||
|
Vector3 unityMax = new Vector3();
|
|||
|
Vector3 unityMin = new Vector3();
|
|||
|
Vector3 dazMax = new Vector3();
|
|||
|
Vector3 dazMin = new Vector3();
|
|||
|
|
|||
|
int i;
|
|||
|
for (i=0; i < unityVerts.Length; i++)
|
|||
|
{
|
|||
|
Vector3 a_vert = m_Skinned.rootBone.TransformPoint(unityVerts[i]);
|
|||
|
// Vector3 a_vert = m_Cloth.transform.TransformPoint(unityVerts[i]);
|
|||
|
// Vector3 a_vert = unityVerts[i];
|
|||
|
a_vert *= 100f;
|
|||
|
|
|||
|
// calc bounds for unity verts
|
|||
|
unityMax.x = (a_vert.x > unityMax.x) ? a_vert.x : unityMax.x;
|
|||
|
unityMax.y = (a_vert.y > unityMax.y) ? a_vert.y : unityMax.y;
|
|||
|
unityMax.z = (a_vert.z > unityMax.z) ? a_vert.z : unityMax.z;
|
|||
|
unityMin.x = (a_vert.x < unityMin.x) ? a_vert.x : unityMin.x;
|
|||
|
unityMin.y = (a_vert.y < unityMin.y) ? a_vert.y : unityMin.y;
|
|||
|
unityMin.z = (a_vert.z < unityMin.z) ? a_vert.z : unityMin.z;
|
|||
|
|
|||
|
//int j;
|
|||
|
//for (j=0; j < numVerts; j++)
|
|||
|
//{
|
|||
|
// Vector3 b_vert = new Vector3();
|
|||
|
// b_vert.x = -dazVertBuffer[(j*3)+0] * 0.01f;
|
|||
|
// b_vert.y = dazVertBuffer[(j*3)+1] * 0.01f;
|
|||
|
// b_vert.z = dazVertBuffer[(j*3)+2] * 0.01f;
|
|||
|
// if (Vector3.Distance(a_vert, b_vert) < 0.001f)
|
|||
|
// {
|
|||
|
// Debug.Log("unity[" + i + "] == daz[" + j + "]");
|
|||
|
// break;
|
|||
|
// }
|
|||
|
//}
|
|||
|
//if (j == numVerts)
|
|||
|
//{
|
|||
|
// Debug.LogError("Could not match skinned[" + i + "] in any vertex of daz[]");
|
|||
|
// calcDazBounds = false;
|
|||
|
//}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
int j;
|
|||
|
for (j = 0; j < numVerts; j++)
|
|||
|
{
|
|||
|
Vector3 a_vert = new Vector3();
|
|||
|
a_vert.y = dazVertBuffer[(j * 3) + 0] * 0.95830578062345271389800693281935f;
|
|||
|
a_vert.x = dazVertBuffer[(j * 3) + 1] * 0.7561518763918325407088556644264f;
|
|||
|
a_vert.z = dazVertBuffer[(j * 3) + 2] * 1.1470080563670463526164516598318f + 14.86038f;
|
|||
|
// calc bounds for unity verts
|
|||
|
dazMax.x = (a_vert.x > dazMax.x) ? a_vert.x : dazMax.x;
|
|||
|
dazMax.y = (a_vert.y > dazMax.y) ? a_vert.y : dazMax.y;
|
|||
|
dazMax.z = (a_vert.z > dazMax.z) ? a_vert.z : dazMax.z;
|
|||
|
dazMin.x = (a_vert.x < dazMin.x) ? a_vert.x : dazMin.x;
|
|||
|
dazMin.y = (a_vert.y < dazMin.y) ? a_vert.y : dazMin.y;
|
|||
|
dazMin.z = (a_vert.z < dazMin.z) ? a_vert.z : dazMin.z;
|
|||
|
}
|
|||
|
Debug.Log("unityBounds: (" + unityMin.x + " to " + unityMax.x + " [" + (unityMax.x - unityMin.x) + "], " + unityMin.y + " to " + unityMax.y + " [" + (unityMax.y - unityMin.y) + "], " + unityMin.z + " to " + unityMax.z + " [" + (unityMax.z - unityMin.z) + "])");
|
|||
|
Debug.Log("dazBounds: (" + dazMin.x + " to " + dazMax.x + " [" + (dazMax.x - dazMin.x) + "], " + dazMin.y + " to " + dazMax.y + " [" + (dazMax.y - dazMin.y) + "], " + dazMin.z + " to " + dazMax.z + " [" + (dazMax.z - dazMin.z) + "])");
|
|||
|
|
|||
|
Debug.Log("Done");
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|