您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
452 行
12 KiB
452 行
12 KiB
using System;
|
|
using UnityEngine;
|
|
using Unity.DemoTeam.Attributes;
|
|
|
|
namespace Unity.DemoTeam.DigitalHuman
|
|
{
|
|
[ExecuteAlways]
|
|
public class SkinAttachment : MeshInstanceBehaviour
|
|
{
|
|
public enum AttachmentMode
|
|
{
|
|
BuildPoses,
|
|
LinkPosesByReference,
|
|
LinkPosesBySpecificIndex,
|
|
}
|
|
|
|
public enum AttachmentType
|
|
{
|
|
Transform,
|
|
Mesh,
|
|
MeshRoots,
|
|
}
|
|
|
|
[HideInInspector] public bool attached;
|
|
[HideInInspector] public bool preserveResolved;
|
|
[HideInInspector] public Vector3 attachedLocalPosition;
|
|
[HideInInspector] public Quaternion attachedLocalRotation;
|
|
|
|
[HideInInspector]
|
|
public SkinAttachmentTarget targetActive;
|
|
|
|
[EditableIf("attached", false)]
|
|
public SkinAttachmentTarget target;
|
|
|
|
[EditableIf("attached", false)]
|
|
public AttachmentType attachmentType = AttachmentType.Transform;
|
|
public AttachmentMode attachmentMode = AttachmentMode.BuildPoses;
|
|
|
|
[EditableIf("attached", false)]
|
|
public SkinAttachment attachmentLink = null;
|
|
|
|
[EditableIf("attachmentMode", AttachmentMode.LinkPosesBySpecificIndex)]
|
|
public int attachmentIndex = -1;
|
|
|
|
[EditableIf("attachmentMode", AttachmentMode.LinkPosesBySpecificIndex)]
|
|
public int attachmentCount = 0;
|
|
|
|
[Header("Debug options")]
|
|
[Range(0, 6)]
|
|
public int debugIndex = 0;
|
|
public bool debugIndexEnabled = false;
|
|
|
|
[Header("Runtime options")]
|
|
public bool forceRecalculateBounds;
|
|
public bool forceRecalculateNormals;
|
|
public bool forceRecalculateTangents;
|
|
|
|
[NonSerialized] public float meshAssetRadius;
|
|
[NonSerialized] public MeshBuffers meshBuffers;
|
|
[NonSerialized] public MeshAdjacency meshAdjacency;
|
|
[NonSerialized] public MeshIslands meshIslands;
|
|
|
|
protected override void OnMeshInstanceCreated()
|
|
{
|
|
meshAssetRadius = meshAsset.bounds.extents.magnitude;// conservative
|
|
|
|
if (meshBuffers == null)
|
|
meshBuffers = new MeshBuffers(meshInstance);
|
|
else
|
|
meshBuffers.LoadFrom(meshInstance);
|
|
|
|
if (meshAdjacency == null)
|
|
meshAdjacency = new MeshAdjacency(meshBuffers);
|
|
else
|
|
meshAdjacency.LoadFrom(meshBuffers);
|
|
|
|
if (meshIslands == null)
|
|
meshIslands = new MeshIslands(meshAdjacency);
|
|
else
|
|
meshIslands.LoadFrom(meshAdjacency);
|
|
}
|
|
|
|
protected override void OnMeshInstanceDeleted()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
void EnsureRegistration()
|
|
{
|
|
if (attached)
|
|
{
|
|
if (target == targetActive)
|
|
return;
|
|
|
|
RemoveRegistration();
|
|
|
|
if (target)
|
|
target.Attach(this);
|
|
}
|
|
else
|
|
{
|
|
RemoveRegistration();
|
|
}
|
|
}
|
|
|
|
void RemoveRegistration()
|
|
{
|
|
if (targetActive)
|
|
targetActive.Detach(this);
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
EnsureMeshInstance();
|
|
EnsureRegistration();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (attached)
|
|
{
|
|
EnsureMeshInstance();
|
|
EnsureRegistration();
|
|
}
|
|
else
|
|
{
|
|
RemoveRegistration();
|
|
RemoveMeshInstance();
|
|
}
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
var forceRecalculateAny = forceRecalculateBounds || forceRecalculateNormals || forceRecalculateTangents;
|
|
if (forceRecalculateAny && meshInstance != null)
|
|
{
|
|
meshInstance.EnableSilentWrites(true);
|
|
|
|
if (forceRecalculateNormals)
|
|
meshInstance.RecalculateNormals();
|
|
if (forceRecalculateTangents)
|
|
meshInstance.RecalculateTangents();
|
|
|
|
meshInstance.EnableSilentWrites(false);
|
|
|
|
if (forceRecalculateBounds)
|
|
meshInstance.RecalculateBounds();
|
|
}
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
RemoveMeshInstance();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
void OnDrawGizmosSelected()
|
|
{
|
|
if (isActiveAndEnabled == false)
|
|
return;
|
|
|
|
if (target == null)
|
|
return;
|
|
|
|
var targetMeshInfo = target.GetCachedMeshInfo();
|
|
if (targetMeshInfo.valid == false)
|
|
return;
|
|
|
|
if (attached)
|
|
{
|
|
if (attachmentType != AttachmentType.Transform)
|
|
{
|
|
Gizmos.matrix = this.transform.localToWorldMatrix;
|
|
Gizmos.DrawWireCube(meshInstance.bounds.center, meshInstance.bounds.extents * 2.0f);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (attachmentType == AttachmentType.Transform)
|
|
{
|
|
var targetPosition = target.transform.InverseTransformPoint(this.transform.position);
|
|
|
|
var closestDist = float.MaxValue;
|
|
var closestNode = -1;
|
|
|
|
if (targetMeshInfo.meshVertexBSP.FindNearest(ref closestDist, ref closestNode, ref targetPosition))
|
|
{
|
|
Gizmos.matrix = target.transform.localToWorldMatrix;
|
|
|
|
var r = targetPosition - target.meshBuffers.vertexPositions[closestNode];
|
|
var d = Vector3.Dot(target.meshBuffers.vertexNormals[closestNode], r);
|
|
var c = (d >= 0.0f) ? Color.cyan : Color.magenta;
|
|
|
|
Gizmos.color = Color.Lerp(Color.clear, c, 0.75f);
|
|
Gizmos.DrawSphere(targetPosition, Mathf.Sqrt(closestDist));
|
|
|
|
Gizmos.color = Color.Lerp(Color.clear, c, 0.75f);
|
|
Gizmos.DrawLine(targetPosition, target.meshBuffers.vertexPositions[closestNode]);
|
|
|
|
target.meshBuffers.DrawTriangles(targetMeshInfo.meshAdjacency.vertexTriangles[closestNode]);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
Gizmos.matrix = this.transform.localToWorldMatrix;
|
|
|
|
if (attachmentType == AttachmentType.Mesh)
|
|
{
|
|
var colorArray = new Color[] { Color.red, Color.green, Color.blue, Color.cyan, Color.magenta, Color.yellow, Color.white };
|
|
var colorIndex = -1;
|
|
|
|
var positions = meshBuffers.vertexPositions;
|
|
|
|
for (int island = 0; island != meshIslands.islandCount; island++)
|
|
{
|
|
colorIndex++;
|
|
colorIndex %= colorArray.Length;
|
|
|
|
if (colorIndex != debugIndex && debugIndexEnabled)
|
|
continue;
|
|
|
|
Gizmos.color = Color.Lerp(Color.clear, colorArray[colorIndex], 0.3f);
|
|
|
|
foreach (var i in meshIslands.islandVertices[island])
|
|
{
|
|
foreach (var j in meshAdjacency.vertexVertices[i])
|
|
{
|
|
Gizmos.DrawLine(positions[i], positions[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (attachmentType == AttachmentType.MeshRoots)
|
|
{
|
|
var colorArray = new Color[] { Color.red, Color.green, Color.blue, Color.cyan, Color.magenta, Color.yellow, Color.white };
|
|
var colorIndex = -1;
|
|
|
|
var positions = meshBuffers.vertexPositions;
|
|
|
|
var targetPositions = new Vector3[positions.Length];
|
|
var subjectToTarget = target.transform.worldToLocalMatrix * this.transform.localToWorldMatrix;
|
|
var targetToSubject = this.transform.worldToLocalMatrix * target.transform.localToWorldMatrix;
|
|
|
|
for (int i = 0; i != positions.Length; i++)
|
|
{
|
|
targetPositions[i] = subjectToTarget.MultiplyPoint3x4(positions[i]);
|
|
}
|
|
|
|
for (int island = 0; island != meshIslands.islandCount; island++)
|
|
{
|
|
colorIndex++;
|
|
colorIndex %= colorArray.Length;
|
|
|
|
if (colorIndex != debugIndex && debugIndexEnabled)
|
|
continue;
|
|
|
|
Gizmos.color = colorArray[colorIndex];
|
|
|
|
// draw the faces
|
|
foreach (int i in meshIslands.islandVertices[island])
|
|
{
|
|
foreach (int j in meshAdjacency.vertexVertices[i])
|
|
{
|
|
Gizmos.DrawLine(positions[i], positions[j]);
|
|
}
|
|
}
|
|
|
|
// draw root-lines
|
|
unsafe
|
|
{
|
|
var rootIdx = new UnsafeArrayInt(positions.Length);
|
|
var rootDir = new UnsafeArrayVector3(positions.Length);
|
|
var rootGen = new UnsafeArrayInt(positions.Length);
|
|
var visitor = new UnsafeBFS(positions.Length);
|
|
|
|
visitor.Clear();
|
|
|
|
// find island roots
|
|
{
|
|
int rootCount = 0;
|
|
|
|
var bestDist0 = float.PositiveInfinity;
|
|
var bestNode0 = -1;
|
|
var bestVert0 = -1;
|
|
|
|
var bestDist1 = float.PositiveInfinity;
|
|
var bestNode1 = -1;
|
|
var bestVert1 = -1;
|
|
|
|
foreach (var i in meshIslands.islandVertices[island])
|
|
{
|
|
var targetDist = float.PositiveInfinity;
|
|
var targetNode = -1;
|
|
|
|
if (targetMeshInfo.meshVertexBSP.FindNearest(ref targetDist, ref targetNode, ref targetPositions[i]))
|
|
{
|
|
// found a root if one or more neighbouring vertices are below
|
|
var bestDist = float.PositiveInfinity;
|
|
var bestNode = -1;
|
|
|
|
foreach (var j in meshAdjacency.vertexVertices[i])
|
|
{
|
|
var targetDelta = targetPositions[j] - target.meshBuffers.vertexPositions[targetNode];
|
|
var targetNormalDist = Vector3.Dot(targetDelta, target.meshBuffers.vertexNormals[targetNode]);
|
|
if (targetNormalDist < 0.0f)
|
|
{
|
|
var d = Vector3.SqrMagnitude(targetDelta);
|
|
if (d < bestDist)
|
|
{
|
|
bestDist = d;
|
|
bestNode = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestNode != -1)
|
|
{
|
|
visitor.Ignore(i);
|
|
rootIdx.val[i] = targetNode;
|
|
rootDir.val[i] = Vector3.Normalize(targetPositions[bestNode] - targetPositions[i]);
|
|
rootGen.val[i] = 0;
|
|
rootCount++;
|
|
}
|
|
else
|
|
{
|
|
rootIdx.val[i] = -1;
|
|
rootGen.val[i] = -1;
|
|
|
|
// see if node qualifies as second choice root
|
|
var targetDelta = targetPositions[i] - target.meshBuffers.vertexPositions[targetNode];
|
|
var targetNormalDist = Mathf.Abs(Vector3.Dot(targetDelta, target.meshBuffers.vertexNormals[targetNode]));
|
|
if (targetNormalDist < bestDist0)
|
|
{
|
|
bestDist1 = bestDist0;
|
|
bestNode1 = bestNode0;
|
|
bestVert1 = bestVert0;
|
|
|
|
bestDist0 = targetNormalDist;
|
|
bestNode0 = targetNode;
|
|
bestVert0 = i;
|
|
}
|
|
else if (targetNormalDist < bestDist1)
|
|
{
|
|
bestDist1 = targetNormalDist;
|
|
bestNode1 = targetNode;
|
|
bestVert1 = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rootCount < 2 && bestVert0 != -1)
|
|
{
|
|
visitor.Ignore(bestVert0);
|
|
rootIdx.val[bestVert0] = bestNode0;
|
|
rootDir.val[bestVert0] = Vector3.Normalize(target.meshBuffers.vertexPositions[bestNode0] - targetPositions[bestVert0]);
|
|
rootGen.val[bestVert0] = 0;
|
|
rootCount++;
|
|
|
|
if (rootCount < 2 && bestVert1 != -1)
|
|
{
|
|
visitor.Ignore(bestVert1);
|
|
rootIdx.val[bestVert1] = bestNode1;
|
|
rootDir.val[bestVert1] = Vector3.Normalize(target.meshBuffers.vertexPositions[bestNode1] - targetPositions[bestVert1]);
|
|
rootGen.val[bestVert1] = 0;
|
|
rootCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// find boundary
|
|
foreach (var i in meshIslands.islandVertices[island])
|
|
{
|
|
if (rootIdx.val[i] != -1)
|
|
continue;
|
|
|
|
foreach (var j in meshAdjacency.vertexVertices[i])
|
|
{
|
|
if (rootIdx.val[j] != -1)
|
|
{
|
|
visitor.Insert(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// propagate roots
|
|
while (visitor.MoveNext())
|
|
{
|
|
var i = visitor.position;
|
|
|
|
var bestDist = float.PositiveInfinity;
|
|
var bestNode = -1;
|
|
|
|
foreach (var j in meshAdjacency.vertexVertices[i])
|
|
{
|
|
if (rootIdx.val[j] != -1)
|
|
{
|
|
var d = -Vector3.Dot(rootDir.val[j], Vector3.Normalize(targetPositions[j] - targetPositions[i]));
|
|
if (d < bestDist)
|
|
{
|
|
bestDist = d;
|
|
bestNode = j;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
visitor.Insert(j);
|
|
}
|
|
}
|
|
|
|
rootIdx.val[i] = rootIdx.val[bestNode];
|
|
rootDir.val[i] = Vector3.Normalize(targetPositions[bestNode] - targetPositions[i]);
|
|
rootGen.val[i] = rootGen.val[bestNode] + 1;
|
|
|
|
Gizmos.color = colorArray[rootGen.val[i] % colorArray.Length];
|
|
Gizmos.DrawSphere(positions[bestNode], 0.0002f);
|
|
Gizmos.DrawSphere(0.5f * (positions[i] + positions[bestNode]), 0.0001f);
|
|
}
|
|
|
|
// draw roots
|
|
foreach (var i in meshIslands.islandVertices[island])
|
|
{
|
|
var root = rootIdx.val[i];
|
|
if (root < 0)
|
|
{
|
|
Debug.Log("i " + i + " has rootIdx " + root);
|
|
Gizmos.DrawLine(positions[i], positions[i] + Vector3.up);
|
|
}
|
|
Gizmos.DrawLine(positions[i], targetToSubject.MultiplyPoint3x4(target.meshBuffers.vertexPositions[root]));
|
|
}
|
|
|
|
// dispose
|
|
visitor.Dispose();
|
|
rootGen.Dispose();
|
|
rootDir.Dispose();
|
|
rootIdx.Dispose();
|
|
}
|
|
|
|
//-------------
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|