您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

851 行
25 KiB

//#define SMR_BAKEMESH_SKIPCALCBOUNDS
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Burst;
namespace Unity.DemoTeam.DigitalHuman
{
using static SkinAttachmentDataBuilder;
[ExecuteAlways]
public class SkinAttachmentTarget : MonoBehaviour
{
public struct MeshInfo
{
public MeshBuffers meshBuffers;
public MeshAdjacency meshAdjacency;
public KdTree3 meshVertexBSP;
public bool valid;
}
[HideInInspector] public List<SkinAttachment> subjects = new List<SkinAttachment>();
[NonSerialized] public Mesh meshBakedSmr;
[NonSerialized] public Mesh meshBakedOrAsset;
[NonSerialized] public MeshBuffers meshBuffers;
[NonSerialized] public Mesh meshBuffersLastAsset;
public SkinAttachmentData attachData;
[Header("Debug options")]
public bool showWireframe = false;
public bool showUVSeams = false;
public bool showResolved = false;
public bool showMouseOver = false;
private MeshInfo cachedMeshInfo;
private int cachedMeshInfoFrame = -1;
private JobHandle[] stagingJobs;
private Vector3[][] stagingData;
private GCHandle[] stagingPins;
void OnEnable()
{
UpdateMeshBuffers();
}
void LateUpdate()
{
if (UpdateMeshBuffers())
{
ResolveSubjects();
}
}
bool UpdateMeshBuffers()
{
meshBakedOrAsset = null;
{
var mf = GetComponent<MeshFilter>();
if (mf != null)
{
meshBakedOrAsset = mf.sharedMesh;
}
var smr = GetComponent<SkinnedMeshRenderer>();
if (smr != null)
{
if (meshBakedSmr == null)
{
meshBakedSmr = new Mesh();
meshBakedSmr.name = "SkinAttachmentTarget(BakeMesh)";
meshBakedSmr.hideFlags = HideFlags.HideAndDontSave & ~HideFlags.DontUnloadUnusedAsset;
meshBakedSmr.MarkDynamic();
}
meshBakedOrAsset = meshBakedSmr;
Profiler.BeginSample("smr.BakeMesh");
{
smr.BakeMesh(meshBakedSmr);
{
meshBakedSmr.bounds = smr.bounds;
}
}
Profiler.EndSample();
}
}
if (meshBakedOrAsset == null)
return false;
if (meshBuffers == null || meshBuffersLastAsset != meshBakedOrAsset)
{
meshBuffers = new MeshBuffers(meshBakedOrAsset);
}
else
{
meshBuffers.LoadPositionsFrom(meshBakedOrAsset);
meshBuffers.LoadNormalsFrom(meshBakedOrAsset);
}
meshBuffersLastAsset = meshBakedOrAsset;
return true;
}
void UpdateMeshInfo(ref MeshInfo info)
{
Profiler.BeginSample("upd-mesh-inf");
if (meshBuffers == null)
{
info.valid = false;
}
else
{
info.meshBuffers = meshBuffers;
const bool weldedAdjacency = false;//TODO enable for more reliable poses along uv seams
if (info.meshAdjacency == null)
info.meshAdjacency = new MeshAdjacency(meshBuffers, weldedAdjacency);
else if (info.meshAdjacency.vertexCount != meshBuffers.vertexCount)
info.meshAdjacency.LoadFrom(meshBuffers, weldedAdjacency);
if (info.meshVertexBSP == null)
info.meshVertexBSP = new KdTree3(meshBuffers.vertexPositions, meshBuffers.vertexCount);
else
info.meshVertexBSP.BuildFrom(meshBuffers.vertexPositions, meshBuffers.vertexCount);
info.valid = true;
}
Profiler.EndSample();
}
public ref MeshInfo GetCachedMeshInfo()
{
int frameIndex = Time.frameCount;
if (frameIndex != cachedMeshInfoFrame)
{
UpdateMeshInfo(ref cachedMeshInfo);
if (cachedMeshInfo.valid)
cachedMeshInfoFrame = frameIndex;
}
return ref cachedMeshInfo;
}
public void AddSubject(SkinAttachment subject)
{
if (subjects.Contains(subject) == false)
subjects.Add(subject);
}
public void RemoveSubject(SkinAttachment subject)
{
if (subjects.Contains(subject))
subjects.Remove(subject);
}
public bool CommitRequired()
{
if (attachData == null)
return false;
for (int i = 0, n = subjects.Count; i != n; i++)
{
if (subjects[i].Checksum() != attachData.Checksum())
return true;
}
return false;
}
public void CommitSubjectsIfRequired()
{
if (CommitRequired())
CommitSubjects();
}
public void CommitSubjects()
{
if (attachData == null)
return;
var meshInfo = GetCachedMeshInfo();
if (meshInfo.valid == false)
return;
attachData.Clear();
{
subjects.RemoveAll(p => (p == null));
// pass 1: build poses
for (int i = 0, n = subjects.Count; i != n; i++)
{
if (subjects[i].attachmentMode == SkinAttachment.AttachmentMode.BuildPoses)
{
subjects[i].RevertVertexData();
CommitSubject(ref meshInfo, subjects[i]);
}
}
// pass 2: reference poses
for (int i = 0, n = subjects.Count; i != n; i++)
{
switch (subjects[i].attachmentMode)
{
case SkinAttachment.AttachmentMode.LinkPosesByReference:
{
if (subjects[i].attachmentLink != null)
{
subjects[i].attachmentType = subjects[i].attachmentLink.attachmentType;
subjects[i].attachmentIndex = subjects[i].attachmentLink.attachmentIndex;
subjects[i].attachmentCount = subjects[i].attachmentLink.attachmentCount;
}
else
{
subjects[i].attachmentIndex = -1;
subjects[i].attachmentCount = 0;
}
}
break;
case SkinAttachment.AttachmentMode.LinkPosesBySpecificIndex:
{
subjects[i].attachmentIndex = Mathf.Clamp(subjects[i].attachmentIndex, 0, attachData.itemCount - 1);
subjects[i].attachmentCount = Mathf.Clamp(subjects[i].attachmentCount, 0, attachData.itemCount - subjects[i].attachmentIndex);
}
break;
}
}
}
attachData.subjectCount = subjects.Count;
attachData.Persist();
for (int i = 0, n = subjects.Count; i != n; i++)
{
subjects[i].checksum0 = attachData.checksum0;
subjects[i].checksum1 = attachData.checksum1;
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(subjects[i]);
UnityEditor.Undo.ClearUndo(subjects[i]);
#endif
}
}
void CommitSubject(ref MeshInfo meshInfo, SkinAttachment subject)
{
Profiler.BeginSample("attach-subj");
var subjectToTarget = this.transform.worldToLocalMatrix * subject.transform.localToWorldMatrix;
switch (subject.attachmentType)
{
case SkinAttachment.AttachmentType.Transform:
unsafe
{
var targetPosition = subjectToTarget.MultiplyPoint3x4(Vector3.zero);
var targetNormal = subjectToTarget.MultiplyVector(Vector3.up);
fixed (int* attachmentIndex = &subject.attachmentIndex)
fixed (int* attachmentCount = &subject.attachmentCount)
{
BuildDataAttachToClosestVertex(attachData, attachmentIndex, attachmentCount, meshInfo, &targetPosition, &targetNormal, 1);
}
}
break;
case SkinAttachment.AttachmentType.Mesh:
unsafe
{
if (subject.meshInstance == null)
break;
var subjectVertexCount = subject.meshBuffers.vertexCount;
var subjectPositions = subject.meshBuffers.vertexPositions;
var subjectNormals = subject.meshBuffers.vertexNormals;
using (var targetPositions = new UnsafeArrayVector3(subjectVertexCount))
using (var targetNormals = new UnsafeArrayVector3(subjectVertexCount))
{
for (int i = 0; i != subjectVertexCount; i++)
{
targetPositions.val[i] = subjectToTarget.MultiplyPoint3x4(subjectPositions[i]);
targetNormals.val[i] = subjectToTarget.MultiplyVector(subjectNormals[i]);
}
fixed (int* attachmentIndex = &subject.attachmentIndex)
fixed (int* attachmentCount = &subject.attachmentCount)
{
BuildDataAttachToClosestVertex(attachData, attachmentIndex, attachmentCount, meshInfo, targetPositions.val, targetNormals.val, subjectVertexCount);
}
}
}
break;
case SkinAttachment.AttachmentType.MeshRoots:
unsafe
{
if (subject.meshInstance == null)
break;
var subjectVertexCount = subject.meshBuffers.vertexCount;
var subjectPositions = subject.meshBuffers.vertexPositions;
var subjectNormals = subject.meshBuffers.vertexPositions;
using (var targetPositions = new UnsafeArrayVector3(subjectVertexCount))
using (var targetNormals = new UnsafeArrayVector3(subjectVertexCount))
using (var targetOffsets = new UnsafeArrayVector3(subjectVertexCount))
using (var targetVertices = new UnsafeArrayInt(subjectVertexCount))
using (var rootIdx = new UnsafeArrayInt(subjectVertexCount))
using (var rootDir = new UnsafeArrayVector3(subjectVertexCount))
using (var rootGen = new UnsafeArrayInt(subjectVertexCount))
using (var visitor = new UnsafeBFS(subjectVertexCount))
{
for (int i = 0; i != subjectVertexCount; i++)
{
targetPositions.val[i] = subjectToTarget.MultiplyPoint3x4(subjectPositions[i]);
targetNormals.val[i] = subjectToTarget.MultiplyVector(subjectNormals[i]);
targetOffsets.val[i] = Vector3.zero;
}
visitor.Clear();
// find island roots
for (int island = 0; island != subject.meshIslands.islandCount; island++)
{
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 subject.meshIslands.islandVertices[island])
{
var targetDist = float.PositiveInfinity;
var targetNode = -1;
if (meshInfo.meshVertexBSP.FindNearest(ref targetDist, ref targetNode, ref targetPositions.val[i]))
{
// found a root if one or more neighbouring vertices are below
var bestDist = float.PositiveInfinity;
var bestNode = -1;
foreach (var j in subject.meshAdjacency.vertexVertices[i])
{
var targetDelta = targetPositions.val[j] - meshBuffers.vertexPositions[targetNode];
var targetNormalDist = Vector3.Dot(targetDelta, 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.val[bestNode] - targetPositions.val[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.val[i] - meshBuffers.vertexPositions[targetNode];
var targetNormalDist = Mathf.Abs(Vector3.Dot(targetDelta, 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(meshBuffers.vertexPositions[bestNode0] - targetPositions.val[bestVert0]);
rootGen.val[bestVert0] = 0;
rootCount++;
if (rootCount < 2 && bestVert1 != -1)
{
visitor.Ignore(bestVert1);
rootIdx.val[bestVert1] = bestNode1;
rootDir.val[bestVert1] = Vector3.Normalize(meshBuffers.vertexPositions[bestNode1] - targetPositions.val[bestVert1]);
rootGen.val[bestVert1] = 0;
rootCount++;
}
}
}
// find boundaries
for (int i = 0; i != subjectVertexCount; i++)
{
if (rootIdx.val[i] != -1)
continue;
foreach (var j in subject.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 subject.meshAdjacency.vertexVertices[i])
{
if (rootIdx.val[j] != -1)
{
var d = -Vector3.Dot(rootDir.val[j], Vector3.Normalize(targetPositions.val[j] - targetPositions.val[i]));
if (d < bestDist)
{
bestDist = d;
bestNode = j;
}
}
else
{
visitor.Insert(j);
}
}
rootIdx.val[i] = rootIdx.val[bestNode];
rootDir.val[i] = Vector3.Normalize(targetPositions.val[bestNode] - targetPositions.val[i]);
rootGen.val[i] = rootGen.val[bestNode] + 1;
targetOffsets.val[i] = targetPositions.val[i] - targetPositions.val[bestNode];
targetPositions.val[i] = targetPositions.val[bestNode];
}
// copy to target vertices
for (int i = 0; i != subjectVertexCount; i++)
{
targetVertices.val[i] = rootIdx.val[i];
}
fixed (int* attachmentIndex = &subject.attachmentIndex)
fixed (int* attachmentCount = &subject.attachmentCount)
{
BuildDataAttachToVertex(attachData, attachmentIndex, attachmentCount, meshInfo, targetPositions.val, targetOffsets.val, targetNormals.val, targetVertices.val, subjectVertexCount);
}
}
}
break;
}
Profiler.EndSample();
}
//--------
// Attach
// moved to SkinAttachmentDataBuilder
//---------
// Resolve
void ResolveSubjects()
{
if (attachData == null)
return;
Profiler.BeginSample("resolve-subj-all");
subjects.RemoveAll(p => p == null);
//Profiler.BeginSample("sort");
//subjects.Sort((a, b) => { return b.attachmentCount.CompareTo(a.attachmentCount); });
//Profiler.EndSample();
int stagingPinsSourceDataCount = 3;
int stagingPinsSourceDataOffset = subjects.Count * 2;
ArrayUtils.ResizeChecked(ref stagingJobs, subjects.Count);
ArrayUtils.ResizeChecked(ref stagingData, subjects.Count * 2);
ArrayUtils.ResizeChecked(ref stagingPins, subjects.Count * 2 + stagingPinsSourceDataCount);
stagingPins[stagingPinsSourceDataOffset + 0] = GCHandle.Alloc(meshBuffers.vertexPositions, GCHandleType.Pinned);
stagingPins[stagingPinsSourceDataOffset + 1] = GCHandle.Alloc(meshBuffers.vertexTangents, GCHandleType.Pinned);
stagingPins[stagingPinsSourceDataOffset + 2] = GCHandle.Alloc(meshBuffers.vertexNormals, GCHandleType.Pinned);
var targetToWorld = Matrix4x4.TRS(this.transform.position, this.transform.rotation, Vector3.one);
// NOTE: targetToWorld specifically excludes scale, since source data (BakeMesh) is already scaled
var targetMeshWorldBounds = meshBakedOrAsset.bounds;
var targetMeshWorldBoundsCenter = targetMeshWorldBounds.center;
var targetMeshWorldBoundsExtent = targetMeshWorldBounds.extents;
for (int i = 0, n = subjects.Count; i != n; i++)
{
var subject = subjects[i];
if (subject.Checksum() != attachData.Checksum())
continue;
int attachmentIndex = subject.attachmentIndex;
int attachmentCount = subject.attachmentCount;
if (attachmentIndex == -1)
continue;
var indexPos = i * 2 + 0;
var indexNrm = i * 2 + 1;
ArrayUtils.ResizeChecked(ref stagingData[indexPos], attachmentCount);
ArrayUtils.ResizeChecked(ref stagingData[indexNrm], attachmentCount);
stagingPins[indexPos] = GCHandle.Alloc(stagingData[indexPos], GCHandleType.Pinned);
stagingPins[indexNrm] = GCHandle.Alloc(stagingData[indexNrm], GCHandleType.Pinned);
unsafe
{
var resolvedPositions = (Vector3*)stagingPins[indexPos].AddrOfPinnedObject().ToPointer();
var resolvedNormals = (Vector3*)stagingPins[indexNrm].AddrOfPinnedObject().ToPointer();
switch (subject.attachmentType)
{
case SkinAttachment.AttachmentType.Transform:
{
stagingJobs[i] = ScheduleResolve(attachmentIndex, attachmentCount, ref targetToWorld, resolvedPositions, resolvedNormals);
}
break;
case SkinAttachment.AttachmentType.Mesh:
case SkinAttachment.AttachmentType.MeshRoots:
{
var targetToSubject = subject.transform.worldToLocalMatrix * targetToWorld;
stagingJobs[i] = ScheduleResolve(attachmentIndex, attachmentCount, ref targetToSubject, resolvedPositions, resolvedNormals);
}
break;
}
}
}
JobHandle.ScheduleBatchedJobs();
while (true)
{
var jobsRunning = false;
for (int i = 0, n = subjects.Count; i != n; i++)
{
var subject = subjects[i];
if (subject.Checksum() != attachData.Checksum())
continue;
var stillRunning = (stagingJobs[i].IsCompleted == false);
if (stillRunning)
{
jobsRunning = true;
continue;
}
var indexPos = i * 2 + 0;
var indexNrm = i * 2 + 1;
var alreadyApplied = (stagingPins[indexPos].IsAllocated == false);
if (alreadyApplied)
continue;
stagingPins[indexPos].Free();
stagingPins[indexNrm].Free();
Profiler.BeginSample("gather-subj");
switch (subject.attachmentType)
{
case SkinAttachment.AttachmentType.Transform:
{
subject.transform.position = stagingData[indexPos][0];
}
break;
case SkinAttachment.AttachmentType.Mesh:
case SkinAttachment.AttachmentType.MeshRoots:
{
if (subject.meshInstance.vertexCount != stagingData[indexPos].Length)
{
Debug.LogError("mismatching vertex- and attachment count", subject);
break;
}
subject.meshInstance.EnableSilentWrites(true);
subject.meshInstance.vertices = stagingData[indexPos];
subject.meshInstance.normals = stagingData[indexNrm];
subject.meshInstance.EnableSilentWrites(false);
//Profiler.BeginSample("recalc-bounds");
//subject.meshInstance.RecalculateBounds();
//Profiler.EndSample();
Profiler.BeginSample("conservative-bounds");
{
//Debug.Log("targetMeshWorldBoundsCenter = " + targetMeshWorldBoundsCenter.ToString("G4") + " (from meshBakedOrAsset = " + meshBakedOrAsset.ToString() + ")");
//Debug.Log("targetMeshWorldBoundsExtents = " + targetMeshWorldBoundsExtents.ToString("G4"));
var worldToSubject = subject.transform.worldToLocalMatrix;
var subjectBoundsCenter = worldToSubject.MultiplyPoint(targetMeshWorldBoundsCenter);
var subjectBoundsRadius = worldToSubject.MultiplyVector(targetMeshWorldBoundsExtent).magnitude + subject.meshAssetRadius;
var subjectBounds = subject.meshInstance.bounds;
{
subjectBounds.center = subjectBoundsCenter;
subjectBounds.extents = subjectBoundsRadius * Vector3.one;
}
subject.meshInstance.bounds = subjectBounds;
}
Profiler.EndSample();
}
break;
}
Profiler.EndSample();
}
if (jobsRunning == false)
break;
}
for (int i = 0; i != stagingPinsSourceDataCount; i++)
{
stagingPins[stagingPinsSourceDataOffset + i].Free();
}
Profiler.EndSample();
}
public unsafe JobHandle ScheduleResolve(int attachmentIndex, int attachmentCount, ref Matrix4x4 resolveTransform, Vector3* resolvedPositions, Vector3* resolvedNormals)
{
fixed (Vector3* meshPositions = meshBuffers.vertexPositions)
fixed (Vector3* meshNormals = meshBuffers.vertexNormals)
fixed (SkinAttachmentItem* attachItem = attachData.item)
fixed (SkinAttachmentPose* attachPose = attachData.pose)
{
var job = new ResolveJob()
{
meshPositions = meshPositions,
meshNormals = meshNormals,
attachItem = attachItem,
attachPose = attachPose,
resolveTransform = resolveTransform,
resolvedPositions = resolvedPositions,
resolvedNormals = resolvedNormals,
attachmentIndex = attachmentIndex,
attachmentCount = attachmentCount,
};
return job.Schedule(attachmentCount, 64);
}
}
[BurstCompile(FloatMode = FloatMode.Fast)]
unsafe struct ResolveJob : IJobParallelFor
{
[NativeDisableUnsafePtrRestriction] public Vector3* meshPositions;
[NativeDisableUnsafePtrRestriction] public Vector3* meshNormals;
[NativeDisableUnsafePtrRestriction] public SkinAttachmentItem* attachItem;
[NativeDisableUnsafePtrRestriction] public SkinAttachmentPose* attachPose;
[NativeDisableUnsafePtrRestriction] public Vector3* resolvedPositions;
[NativeDisableUnsafePtrRestriction] public Vector3* resolvedNormals;
public Matrix4x4 resolveTransform;
public int attachmentIndex;
public int attachmentCount;
//TODO this is still too slow, speed it up
public void Execute(int i)
{
var targetBlended = new Vector3(0.0f, 0.0f, 0.0f);
var targetWeights = 0.0f;
SkinAttachmentItem desc = attachItem[attachmentIndex + i];
var poseIndex0 = desc.poseIndex;
var poseIndexN = desc.poseIndex + desc.poseCount;
for (int poseIndex = poseIndex0; poseIndex != poseIndexN; poseIndex++)
{
SkinAttachmentPose pose = attachPose[poseIndex];
var p0 = meshPositions[pose.v0];
var p1 = meshPositions[pose.v1];
var p2 = meshPositions[pose.v2];
var v0v1 = p1 - p0;
var v0v2 = p2 - p0;
var triangleNormal = Vector3.Cross(v0v1, v0v2);
var triangleArea = Vector3.Magnitude(triangleNormal);
triangleNormal /= triangleArea;
triangleArea *= 0.5f;
//var n0 = meshNormals[pose.v0];
//var n1 = meshNormals[pose.v1];
//var n2 = meshNormals[pose.v2];
var targetProjected = pose.targetCoord.Resolve(ref p0, ref p1, ref p2);
//var targetNormal = pose.targetCoord.Resolve(n0, n1, n2);
var target = targetProjected + triangleNormal * pose.targetDist;
//TODO back to orig. area?
targetBlended += /*pose.area*/triangleArea * target;
targetWeights += /*pose.area*/triangleArea;
}
var targetNormalRot = Quaternion.FromToRotation(desc.baseNormal, meshNormals[desc.baseVertex]);
var targetNormal = targetNormalRot * desc.targetNormal;
var targetOffset = targetNormalRot * desc.targetOffset;
resolvedPositions[i] = resolveTransform.MultiplyPoint3x4(targetBlended / targetWeights + targetOffset);
resolvedNormals[i] = resolveTransform.MultiplyVector(targetNormal);
}
}
//--------
// Gizmos
//PACKAGETODO move these to SkinAttachmentTargetEditor
#if UNITY_EDITOR
public void OnDrawGizmos()
{
var activeGO = UnityEditor.Selection.activeGameObject;
if (activeGO == null)
return;
if (activeGO != this.gameObject && activeGO.GetComponent<SkinAttachment>() == null)
return;
Gizmos.matrix = this.transform.localToWorldMatrix;
if (showWireframe)
{
Profiler.BeginSample("show-wire");
{
var meshVertexCount = meshBuffers.vertexCount;
var meshPositions = meshBuffers.vertexPositions;
var meshNormals = meshBuffers.vertexNormals;
Gizmos.color = Color.Lerp(Color.clear, Color.green, 0.25f);
Gizmos.DrawWireMesh(meshBakedOrAsset, 0);
Gizmos.color = Color.red;
for (int i = 0; i != meshVertexCount; i++)
{
Gizmos.DrawRay(meshPositions[i], meshNormals[i] * 0.001f);// 1mm
}
}
Profiler.EndSample();
}
if (showUVSeams)
{
Profiler.BeginSample("show-seams");
{
Gizmos.color = Color.cyan;
var weldedAdjacency = new MeshAdjacency(meshBuffers, true);
for (int i = 0; i != weldedAdjacency.vertexCount; i++)
{
if (weldedAdjacency.vertexWelded.GetCount(i) > 0)
{
bool seam = false;
foreach (var j in weldedAdjacency.vertexVertices[i])
{
if (weldedAdjacency.vertexWelded.GetCount(j) > 0)
{
seam = true;
if (i < j)
{
Gizmos.DrawLine(meshBuffers.vertexPositions[i], meshBuffers.vertexPositions[j]);
}
}
}
if (!seam)
{
Gizmos.color = Color.magenta;
Gizmos.DrawRay(meshBuffers.vertexPositions[i], meshBuffers.vertexNormals[i] * 0.003f);
Gizmos.color = Color.cyan;
}
}
}
}
Profiler.EndSample();
}
if (showResolved)
{
Profiler.BeginSample("show-resolve");
unsafe
{
var attachmentIndex = 0;
var attachmentCount = attachData.itemCount;
using (var resolvedPositions = new UnsafeArrayVector3(attachmentCount))
using (var resolvedNormals = new UnsafeArrayVector3(attachmentCount))
{
var resolveTransform = Matrix4x4.identity;
var resolveJob = ScheduleResolve(attachmentIndex, attachmentCount, ref resolveTransform, resolvedPositions.val, resolvedNormals.val);
JobHandle.ScheduleBatchedJobs();
resolveJob.Complete();
Gizmos.color = Color.yellow;
for (int i = 0; i != attachmentCount; i++)
{
Gizmos.DrawSphere(resolvedPositions.val[i], 0.0002f);
}
}
}
Profiler.EndSample();
}
}
#endif
}
}