您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
566 行
14 KiB
566 行
14 KiB
using System;
|
|
using UnityEngine;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
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 Vector3 attachedLocalPosition;
|
|
[HideInInspector] public Quaternion attachedLocalRotation;
|
|
|
|
[HideInInspector]
|
|
public SkinAttachmentTarget targetActive;
|
|
|
|
[EditableIf("attached", false)]
|
|
public SkinAttachmentTarget target;
|
|
|
|
[EditableIf("attached", false)]
|
|
public AttachmentType attachmentType = AttachmentType.Transform;
|
|
|
|
[EditableIf("attached", false)]
|
|
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;
|
|
|
|
[HideInInspector]
|
|
public ulong checksum0 = 0;
|
|
[HideInInspector]
|
|
public ulong checksum1 = 0;
|
|
|
|
[Header("Debug options")]
|
|
[Range(0, 6)]
|
|
public int debugIndex = 0;
|
|
public bool debugIndexEnabled = false;
|
|
public bool debugBounds = 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;
|
|
|
|
[NonSerialized] public Transform skinningBone;
|
|
[NonSerialized] public Matrix4x4 skinningBoneBindPose;
|
|
[NonSerialized] public Matrix4x4 skinningBoneBindPoseInverse;
|
|
|
|
void DiscoverSkinningBone()
|
|
{
|
|
skinningBone = null;
|
|
skinningBoneBindPose = Matrix4x4.identity;
|
|
skinningBoneBindPoseInverse = Matrix4x4.identity;
|
|
|
|
// search for skinning bone
|
|
var smr = GetComponent<SkinnedMeshRenderer>();
|
|
if (smr != null)
|
|
{
|
|
int skinningBoneIndex = -1;
|
|
|
|
unsafe
|
|
{
|
|
var boneWeights = meshAsset.GetAllBoneWeights();
|
|
var boneWeightPtr = (BoneWeight1*)boneWeights.GetUnsafeReadOnlyPtr();
|
|
|
|
for (int i = 0; i != boneWeights.Length; i++)
|
|
{
|
|
if (boneWeightPtr[i].weight > 0.0f)
|
|
{
|
|
if (skinningBoneIndex == -1)
|
|
skinningBoneIndex = boneWeightPtr[i].boneIndex;
|
|
|
|
if (skinningBoneIndex != boneWeightPtr[i].boneIndex)
|
|
{
|
|
skinningBoneIndex = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (skinningBoneIndex != -1)
|
|
{
|
|
skinningBone = smr.bones[skinningBoneIndex];
|
|
skinningBoneBindPose = meshInstance.bindposes[skinningBoneIndex];
|
|
skinningBoneBindPoseInverse = skinningBoneBindPose.inverse;
|
|
//Debug.Log("discovered skinning bone for " + this.name + " : " + skinningBone.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
DiscoverSkinningBone();
|
|
}
|
|
|
|
protected override void OnMeshInstanceDeleted()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
public Hash128 Checksum()
|
|
{
|
|
return new Hash128(checksum0, checksum1);
|
|
}
|
|
|
|
public void RevertVertexData()
|
|
{
|
|
if (meshAsset != null)
|
|
{
|
|
if (meshBuffers == null)
|
|
meshBuffers = new MeshBuffers(meshAsset);
|
|
else
|
|
meshBuffers.LoadFrom(meshAsset);
|
|
}
|
|
}
|
|
|
|
public void Attach(bool storePositionRotation = true)
|
|
{
|
|
EnsureMeshInstance();
|
|
|
|
if (targetActive != null)
|
|
targetActive.RemoveSubject(this);
|
|
|
|
targetActive = target;
|
|
targetActive.AddSubject(this);
|
|
|
|
if (storePositionRotation)
|
|
{
|
|
attachedLocalPosition = transform.localPosition;
|
|
attachedLocalRotation = transform.localRotation;
|
|
}
|
|
|
|
attached = true;
|
|
}
|
|
|
|
public void Detach(bool revertPositionRotation = true)
|
|
{
|
|
RemoveMeshInstance();
|
|
|
|
if (targetActive != null)
|
|
targetActive.RemoveSubject(this);
|
|
|
|
if (revertPositionRotation)
|
|
{
|
|
transform.localPosition = attachedLocalPosition;
|
|
transform.localRotation = attachedLocalRotation;
|
|
}
|
|
|
|
attached = false;
|
|
}
|
|
|
|
void ValidateAttachedState()
|
|
{
|
|
if (attached)
|
|
{
|
|
if (targetActive != null && targetActive == target)
|
|
{
|
|
EnsureMeshInstance();
|
|
}
|
|
else
|
|
{
|
|
Detach();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RemoveMeshInstance();
|
|
}
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
ValidateAttachedState();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
ValidateAttachedState();
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
var forceRecalculateAny = forceRecalculateBounds || forceRecalculateNormals || forceRecalculateTangents;
|
|
if (forceRecalculateAny && meshInstance != null)
|
|
{
|
|
if (forceRecalculateTangents)
|
|
meshInstance.SilentlyRecalculateTangents();
|
|
if (forceRecalculateNormals)
|
|
meshInstance.SilentlyRecalculateNormals();
|
|
if (forceRecalculateBounds)
|
|
meshInstance.SilentlyRecalculateBounds();
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
//TODO get rid of duplicate code
|
|
|
|
if (attached)
|
|
{
|
|
if (attachmentType != AttachmentType.Transform && debugBounds)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (meshBuffers == null)
|
|
{
|
|
EnsureMeshInstance();
|
|
}
|
|
|
|
if (skinningBone != null)
|
|
Gizmos.matrix = skinningBone.localToWorldMatrix * skinningBoneBindPose;
|
|
else
|
|
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];
|
|
|
|
Matrix4x4 targetToWorld = Matrix4x4.TRS(target.transform.position, target.transform.rotation, Vector3.one);
|
|
Matrix4x4 subjectToTarget;
|
|
Matrix4x4 targetToSubject;
|
|
{
|
|
if (skinningBone != null)
|
|
{
|
|
subjectToTarget = target.transform.worldToLocalMatrix * (skinningBone.localToWorldMatrix * skinningBoneBindPose);
|
|
targetToSubject = (skinningBoneBindPoseInverse * skinningBone.worldToLocalMatrix) * targetToWorld;
|
|
}
|
|
else
|
|
{
|
|
subjectToTarget = target.transform.worldToLocalMatrix * this.transform.localToWorldMatrix;
|
|
targetToSubject = this.transform.worldToLocalMatrix * targetToWorld;
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|