10 [RequireComponent(typeof(MeshFilter))]
19 bool viewFrustrumCulling =
true;
26 public Vector4 position;
27 public Vector4 normal;
28 public Vector4 tangent;
30 public int boneIndex0;
31 public int boneIndex1;
32 public int boneIndex2;
33 public int boneIndex3;
40 public float compensation_coef;
48 public Vector4 position;
49 public Vector4 normal;
50 public Vector4 tangent;
55 public Quaternion rotationQuaternion;
56 public Vector4 position;
59 const int numthreads = 1024;
60 const int textureWidth = 1024;
68 public ComputeShader shaderComputeBoneDQ;
69 public ComputeShader shaderDQBlend;
70 public ComputeShader shaderApplyMorph;
75 public bool started {
get;
private set; } =
false;
77 Matrix4x4[] poseMatrices;
79 ComputeBuffer bufPoseMatrices;
80 ComputeBuffer bufSkinnedDq;
81 ComputeBuffer bufBindDq;
83 ComputeBuffer bufVertInfo;
84 ComputeBuffer bufMorphTemp_1;
85 ComputeBuffer bufMorphTemp_2;
87 ComputeBuffer bufBoneDirections;
89 ComputeBuffer[] arrBufMorphDeltas;
99 this._mf = this.GetComponent<MeshFilter>();
111 if (this._mr ==
null)
113 this._mr = this.GetComponent<MeshRenderer>();
114 if (this._mr ==
null)
116 this._mr = this.gameObject.AddComponent<MeshRenderer>();
125 SkinnedMeshRenderer smr
129 if (this._smr ==
null)
131 this._smr = this.GetComponent<SkinnedMeshRenderer>();
137 SkinnedMeshRenderer _smr;
139 MaterialPropertyBlock materialPropertyBlock;
142 Matrix4x4[] bindPoses;
155 RenderTexture rtSkinnedData_1;
156 RenderTexture rtSkinnedData_2;
157 RenderTexture rtSkinnedData_3;
159 int kernelHandleComputeBoneDQ;
160 int kernelHandleDQBlend;
161 int kernelHandleApplyMorph;
169 if (this.viewFrustrumCulling == viewFrustrumculling)
172 this.viewFrustrumCulling = viewFrustrumculling;
175 UpdateViewFrustrumCulling();
184 return this.viewFrustrumCulling;
187 void UpdateViewFrustrumCulling()
189 if (this.viewFrustrumCulling)
190 this.mf.sharedMesh.bounds = this.smr.localBounds;
192 this.mf.sharedMesh.bounds =
new Bounds(Vector3.zero, Vector3.one * 100000000);
203 float[] weights =
new float[this.morphWeights.Length];
204 for (
int i = 0; i < weights.Length; i++)
206 weights[i] = this.morphWeights[i];
220 if (weights.Length !=
this.morphWeights.Length)
222 throw new System.ArgumentException(
223 "An array of weights must contain the number of elements " +
224 $
"equal to the number of available blendshapes. Currently " +
225 $
"{this.morphWeights.Length} blendshapes ara available but {weights.Length} weights were passed."
229 for (
int i = 0; i < weights.Length; i++)
231 this.morphWeights[i] = weights[i];
248 this.GetComponent<SkinnedMeshRenderer>().SetBlendShapeWeight(index, weight);
252 if (index < 0 || index >= this.morphWeights.Length)
254 throw new System.IndexOutOfRangeException(
"Blend shape index out of range");
257 this.morphWeights[index] = weight;
273 return this.GetComponent<SkinnedMeshRenderer>().GetBlendShapeWeight(index);
276 if (index < 0 || index >= this.morphWeights.Length)
278 throw new System.IndexOutOfRangeException(
"Blend shape index out of range");
281 return this.morphWeights[index];
296 return this.smr.sharedMesh;
299 return this.mf.sharedMesh;
313 var vertInfos =
new VertexInfo[this.mf.sharedMesh.vertexCount];
314 this.bufVertInfo.GetData(vertInfos);
316 for (
int i = 0; i < vertInfos.Length; i++)
318 Matrix4x4 bindPose = this.bindPoses[vertInfos[i].boneIndex0].inverse;
319 Quaternion boneBindRotation = bindPose.ExtractRotation();
321 Vector3 bonePosition = bindPose.ExtractPosition();
322 Vector3 toBone = bonePosition - (Vector3)vertInfos[i].position;
324 vertInfos[i].compensation_coef = Vector3.Cross(toBone, boneDirection).magnitude;
327 this.bufVertInfo.SetData(vertInfos);
331 int GetVertexTextureHeight(
int vertexCount,
int textureWidth)
333 int textureHeight = this.mf.sharedMesh.vertexCount / textureWidth;
334 if (this.mf.sharedMesh.vertexCount % textureWidth != 0)
338 return textureHeight;
341 void GrabMeshFromSkinnedMeshRenderer()
343 this.ReleaseBuffers();
345 this.mf.sharedMesh = this.smr.sharedMesh;
346 this.bindPoses = this.mf.sharedMesh.bindposes;
348 this.arrBufMorphDeltas =
new ComputeBuffer[this.mf.sharedMesh.blendShapeCount];
350 this.morphWeights =
new float[this.mf.sharedMesh.blendShapeCount];
352 var deltaVertices =
new Vector3[this.mf.sharedMesh.vertexCount];
353 var deltaNormals =
new Vector3[this.mf.sharedMesh.vertexCount];
354 var deltaTangents =
new Vector3[this.mf.sharedMesh.vertexCount];
356 var deltaVertInfos =
new MorphDelta[this.mf.sharedMesh.vertexCount];
358 for (
int i = 0; i < this.mf.sharedMesh.blendShapeCount; i++)
360 this.mf.sharedMesh.GetBlendShapeFrameVertices(i, 0, deltaVertices, deltaNormals, deltaTangents);
362 this.arrBufMorphDeltas[i] =
new ComputeBuffer(this.mf.sharedMesh.vertexCount,
sizeof(
float) * 12);
364 for (
int k = 0; k < this.mf.sharedMesh.vertexCount; k++)
366 deltaVertInfos[k].position = deltaVertices !=
null ? deltaVertices[k] : Vector3.zero;
367 deltaVertInfos[k].normal = deltaNormals !=
null ? deltaNormals[k] : Vector3.zero;
368 deltaVertInfos[k].tangent = deltaTangents !=
null ? deltaTangents[k] : Vector3.zero;
371 this.arrBufMorphDeltas[i].SetData(deltaVertInfos);
374 Material[] materials = this.smr.sharedMaterials;
375 for (
int i = 0; i < materials.Length; i++)
377 materials[i].SetInt(
"_DoSkinning", 1);
379 this.mr.materials = materials;
381 this.shaderDQBlend.SetInt(
"textureWidth", textureWidth);
383 this.poseMatrices =
new Matrix4x4[this.mf.sharedMesh.bindposes.Length];
387 int textureHeight = GetVertexTextureHeight(this.mf.sharedMesh.vertexCount, textureWidth);
389 this.rtSkinnedData_1 =
new RenderTexture(textureWidth, textureHeight, 0, RenderTextureFormat.ARGBFloat)
391 filterMode = FilterMode.Point,
392 enableRandomWrite =
true
394 this.rtSkinnedData_1.Create();
395 this.shaderDQBlend.SetTexture(this.kernelHandleDQBlend,
"skinned_data_1", this.rtSkinnedData_1);
397 this.rtSkinnedData_2 =
new RenderTexture(textureWidth, textureHeight, 0, RenderTextureFormat.ARGBFloat)
399 filterMode = FilterMode.Point,
400 enableRandomWrite =
true
402 this.rtSkinnedData_2.Create();
403 this.shaderDQBlend.SetTexture(this.kernelHandleDQBlend,
"skinned_data_2", this.rtSkinnedData_2);
405 this.rtSkinnedData_3 =
new RenderTexture(textureWidth, textureHeight, 0, RenderTextureFormat.RGFloat)
407 filterMode = FilterMode.Point,
408 enableRandomWrite =
true
410 this.rtSkinnedData_3.Create();
411 this.shaderDQBlend.SetTexture(this.kernelHandleDQBlend,
"skinned_data_3", this.rtSkinnedData_3);
413 this.bufPoseMatrices =
new ComputeBuffer(this.mf.sharedMesh.bindposes.Length,
sizeof(
float) * 16);
414 this.shaderComputeBoneDQ.SetBuffer(this.kernelHandleComputeBoneDQ,
"pose_matrices", this.bufPoseMatrices);
416 this.bufSkinnedDq =
new ComputeBuffer(this.mf.sharedMesh.bindposes.Length,
sizeof(
float) * 8);
417 this.shaderComputeBoneDQ.SetBuffer(this.kernelHandleComputeBoneDQ,
"skinned_dual_quaternions", this.bufSkinnedDq);
418 this.shaderDQBlend.SetBuffer(this.kernelHandleDQBlend,
"skinned_dual_quaternions", this.bufSkinnedDq);
420 this.bufBoneDirections =
new ComputeBuffer(this.mf.sharedMesh.bindposes.Length,
sizeof(
float) * 4);
421 this.shaderComputeBoneDQ.SetBuffer(this.kernelHandleComputeBoneDQ,
"bone_directions", this.bufBoneDirections);
422 this.shaderDQBlend.SetBuffer(this.kernelHandleDQBlend,
"bone_directions", this.bufBoneDirections);
424 this.bufVertInfo =
new ComputeBuffer(this.mf.sharedMesh.vertexCount,
sizeof(
float) * 16 +
sizeof(
int) * 4 +
sizeof(
float));
425 var vertInfos =
new VertexInfo[this.mf.sharedMesh.vertexCount];
426 Vector3[] vertices = this.mf.sharedMesh.vertices;
427 Vector3[] normals = this.mf.sharedMesh.normals;
428 Vector4[] tangents = this.mf.sharedMesh.tangents;
429 BoneWeight[] boneWeights = this.mf.sharedMesh.boneWeights;
430 for (
int i = 0; i < vertInfos.Length; i++)
432 vertInfos[i].position = vertices[i];
434 vertInfos[i].boneIndex0 = boneWeights[i].boneIndex0;
435 vertInfos[i].boneIndex1 = boneWeights[i].boneIndex1;
436 vertInfos[i].boneIndex2 = boneWeights[i].boneIndex2;
437 vertInfos[i].boneIndex3 = boneWeights[i].boneIndex3;
439 vertInfos[i].weight0 = boneWeights[i].weight0;
440 vertInfos[i].weight1 = boneWeights[i].weight1;
441 vertInfos[i].weight2 = boneWeights[i].weight2;
442 vertInfos[i].weight3 = boneWeights[i].weight3;
446 Matrix4x4 bindPose = this.bindPoses[vertInfos[i].boneIndex0].inverse;
447 Quaternion boneBindRotation = bindPose.ExtractRotation();
449 Vector3 bonePosition = bindPose.ExtractPosition();
450 Vector3 toBone = bonePosition - (Vector3)vertInfos[i].position;
452 vertInfos[i].compensation_coef = Vector3.Cross(toBone, boneDirection).magnitude;
455 if (normals.Length > 0)
457 for (
int i = 0; i < vertInfos.Length; i++)
459 vertInfos[i].normal = normals[i];
463 if (tangents.Length > 0)
465 for (
int i = 0; i < vertInfos.Length; i++)
467 vertInfos[i].tangent = tangents[i];
471 this.bufVertInfo.SetData(vertInfos);
472 this.shaderDQBlend.SetBuffer(this.kernelHandleDQBlend,
"vertex_infos", this.bufVertInfo);
474 this.bufMorphTemp_1 =
new ComputeBuffer(this.mf.sharedMesh.vertexCount,
sizeof(
float) * 16 +
sizeof(
int) * 4 +
sizeof(
float));
475 this.bufMorphTemp_2 =
new ComputeBuffer(this.mf.sharedMesh.vertexCount,
sizeof(
float) * 16 +
sizeof(
int) * 4 +
sizeof(
float));
479 Matrix4x4[] bindPoses = this.mf.sharedMesh.bindposes;
480 var bindDqs =
new DualQuaternion[bindPoses.Length];
481 for (
int i = 0; i < bindPoses.Length; i++)
483 bindDqs[i].rotationQuaternion = bindPoses[i].ExtractRotation();
484 bindDqs[i].position = bindPoses[i].ExtractPosition();
487 this.bufBindDq =
new ComputeBuffer(bindDqs.Length,
sizeof(
float) * 8);
488 this.bufBindDq.SetData(bindDqs);
489 this.shaderComputeBoneDQ.SetBuffer(this.kernelHandleComputeBoneDQ,
"bind_dual_quaternions", this.bufBindDq);
491 this.UpdateViewFrustrumCulling();
497 ComputeBuffer bufMorphedVertexInfos = this.GetMorphedVertexInfos(
499 ref this.bufMorphTemp_1,
500 ref this.bufMorphTemp_2,
501 this.arrBufMorphDeltas,
505 this.shaderDQBlend.SetBuffer(this.kernelHandleDQBlend,
"vertex_infos", bufMorphedVertexInfos);
508 ComputeBuffer GetMorphedVertexInfos(ComputeBuffer bufOriginal, ref ComputeBuffer bufTemp_1, ref ComputeBuffer bufTemp_2, ComputeBuffer[] arrBufDelta,
float[] weights)
510 ComputeBuffer bufSource = bufOriginal;
512 for (
int i = 0; i < weights.Length; i++)
519 if (arrBufDelta[i] ==
null)
521 throw new System.NullReferenceException();
524 this.shaderApplyMorph.SetBuffer(this.kernelHandleApplyMorph,
"source", bufSource);
525 this.shaderApplyMorph.SetBuffer(this.kernelHandleApplyMorph,
"target", bufTemp_1);
526 this.shaderApplyMorph.SetBuffer(this.kernelHandleApplyMorph,
"delta", arrBufDelta[i]);
527 this.shaderApplyMorph.SetFloat(
"weight", weights[i] / 100f);
529 int numThreadGroups = bufSource.count / numthreads;
530 if (bufSource.count % numthreads != 0)
535 this.shaderApplyMorph.Dispatch(this.kernelHandleApplyMorph, numThreadGroups, 1, 1);
537 bufSource = bufTemp_1;
538 bufTemp_1 = bufTemp_2;
539 bufTemp_2 = bufSource;
545 void ReleaseBuffers()
547 this.bufBindDq?.Release();
548 this.bufPoseMatrices?.Release();
549 this.bufSkinnedDq?.Release();
551 this.bufVertInfo?.Release();
552 this.bufMorphTemp_1?.Release();
553 this.bufMorphTemp_2?.Release();
555 this.bufBoneDirections?.Release();
557 if (this.arrBufMorphDeltas !=
null)
559 for (
int i = 0; i < this.arrBufMorphDeltas.Length; i++)
561 this.arrBufMorphDeltas[i]?.Release();
567 this.ReleaseBuffers();
573 this.materialPropertyBlock =
new MaterialPropertyBlock();
575 this.shaderComputeBoneDQ = (ComputeShader)Instantiate(this.shaderComputeBoneDQ);
576 this.shaderDQBlend = (ComputeShader)Instantiate(this.shaderDQBlend);
577 this.shaderApplyMorph = (ComputeShader)Instantiate(this.shaderApplyMorph);
579 this.kernelHandleComputeBoneDQ = this.shaderComputeBoneDQ.FindKernel(
"CSMain");
580 this.kernelHandleDQBlend = this.shaderDQBlend.FindKernel(
"CSMain");
581 this.kernelHandleApplyMorph = this.shaderApplyMorph.FindKernel(
"CSMain");
583 this.bones = this.smr.bones;
586 this.GrabMeshFromSkinnedMeshRenderer();
588 for (
int i = 0; i < this.morphWeights.Length; i++)
590 this.morphWeights[i] = this.smr.GetBlendShapeWeight(i);
593 this.smr.enabled =
false;
599 if (this.mr.isVisible ==
false)
604 this.mf.sharedMesh.MarkDynamic();
607 for (
int i = 0; i < this.bones.Length; i++)
609 this.poseMatrices[i] = this.bones[i].localToWorldMatrix;
612 this.bufPoseMatrices.SetData(this.poseMatrices);
616 int numThreadGroups = this.bones.Length / numthreads;
617 numThreadGroups += this.bones.Length % numthreads == 0 ? 0 : 1;
619 this.shaderComputeBoneDQ.SetVector(
"boneOrientation", this.boneOrientationVector);
620 this.shaderComputeBoneDQ.SetMatrix(
622 this.transform.worldToLocalMatrix
624 this.shaderComputeBoneDQ.Dispatch(this.kernelHandleComputeBoneDQ, numThreadGroups, 1, 1);
626 numThreadGroups = this.mf.sharedMesh.vertexCount / numthreads;
627 numThreadGroups += this.mf.sharedMesh.vertexCount % numthreads == 0 ? 0 : 1;
629 this.shaderDQBlend.SetFloat(
"compensation_coef", this.bulgeCompensation);
630 this.shaderDQBlend.Dispatch(this.kernelHandleDQBlend, numThreadGroups, 1, 1);
632 this.materialPropertyBlock.SetTexture(
"skinned_data_1", this.rtSkinnedData_1);
633 this.materialPropertyBlock.SetTexture(
"skinned_data_2", this.rtSkinnedData_2);
634 this.materialPropertyBlock.SetTexture(
"skinned_data_3", this.rtSkinnedData_3);
635 this.materialPropertyBlock.SetInt(
"skinned_tex_height", GetVertexTextureHeight(this.mf.sharedMesh.vertexCount, textureWidth));
636 this.materialPropertyBlock.SetInt(
"skinned_tex_width", textureWidth);
638 this.mr.SetPropertyBlock(this.materialPropertyBlock);