您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
281 行
9.8 KiB
281 行
9.8 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using UniGLTF;
|
|
using Unity.Collections;
|
|
using UnityEngine;
|
|
|
|
namespace VrmLib
|
|
{
|
|
// Bone skinning
|
|
public class Skin : GltfId
|
|
{
|
|
public BufferAccessor InverseMatrices;
|
|
|
|
public Node Root;
|
|
|
|
public List<Node> Joints = new List<Node>();
|
|
|
|
Matrix4x4[] m_matrices;
|
|
public Matrix4x4[] SkinningMatrices => m_matrices;
|
|
|
|
ushort m_indexOfRoot = ushort.MaxValue;
|
|
|
|
public Skin()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// BoneSkinningもしくはMorphTargetの適用
|
|
/// <summary>
|
|
public void Skinning(INativeArrayManager arrayManager, VertexBuffer vertexBuffer = null)
|
|
{
|
|
m_indexOfRoot = (ushort)Joints.IndexOf(Root);
|
|
var addRoot = Root != null && m_indexOfRoot == ushort.MaxValue;
|
|
if (addRoot)
|
|
{
|
|
m_indexOfRoot = (ushort)Joints.Count;
|
|
Joints.Add(Root);
|
|
}
|
|
|
|
if (m_matrices == null)
|
|
{
|
|
m_matrices = new Matrix4x4[Joints.Count];
|
|
}
|
|
|
|
if (InverseMatrices == null)
|
|
{
|
|
CalcInverseMatrices(arrayManager);
|
|
}
|
|
else
|
|
{
|
|
if (addRoot)
|
|
{
|
|
var inverseArray = InverseMatrices.Bytes.Reinterpret<Matrix4x4>(1);
|
|
var concat = inverseArray.Concat(new[] { Root.InverseMatrix }).ToArray();
|
|
InverseMatrices.Assign(concat);
|
|
}
|
|
}
|
|
|
|
var inverse = InverseMatrices.GetSpan<Matrix4x4>();
|
|
|
|
// if (Root != null)
|
|
// {
|
|
// var rootInverse = Root.InverseMatrix;
|
|
// var root = Root.Matrix;
|
|
// for (int i = 0; i < m_matrices.Length; ++i)
|
|
// {
|
|
// m_matrices[i] = inverse[i] * Joints[i].Matrix * rootInverse;
|
|
// }
|
|
// }
|
|
// else
|
|
{
|
|
for (int i = 0; i < m_matrices.Length; ++i)
|
|
{
|
|
var inv = i < inverse.Length ? inverse[i] : Joints[i].InverseMatrix;
|
|
m_matrices[i] = inv * Joints[i].Matrix;
|
|
}
|
|
}
|
|
|
|
if (vertexBuffer != null)
|
|
{
|
|
Apply(arrayManager, vertexBuffer);
|
|
}
|
|
}
|
|
|
|
void Apply(INativeArrayManager arrayManager, VertexBuffer vertexBuffer)
|
|
{
|
|
var dstPosition = vertexBuffer.Positions.Bytes.Reinterpret<Vector3>(1);
|
|
// Span<Vector3> emptyNormal = stackalloc Vector3[0];
|
|
Apply(arrayManager, vertexBuffer, dstPosition, vertexBuffer.Normals != null ? vertexBuffer.Normals.Bytes.Reinterpret<Vector3>(1) : default);
|
|
}
|
|
|
|
public void Apply(INativeArrayManager arrayManager, VertexBuffer vertexBuffer, NativeArray<Vector3> dstPosition, NativeArray<Vector3> dstNormal)
|
|
{
|
|
var jointsBuffer = vertexBuffer.Joints;
|
|
var joints = (jointsBuffer != null || jointsBuffer.Count == 0)
|
|
? jointsBuffer.GetAsSkinJointsArray()
|
|
: arrayManager.CreateNativeArray<SkinJoints>(vertexBuffer.Count) // when MorphTarget only
|
|
;
|
|
|
|
var weightsBuffer = vertexBuffer.Weights;
|
|
var weights = (weightsBuffer != null || weightsBuffer.Count == 0)
|
|
? weightsBuffer.GetAsVector4Array()
|
|
: arrayManager.CreateNativeArray<Vector4>(vertexBuffer.Count) // when MorphTarget only
|
|
;
|
|
|
|
var positionBuffer = vertexBuffer.Positions;
|
|
var position = positionBuffer.Bytes.Reinterpret<Vector3>(1);
|
|
|
|
bool useNormal = false;
|
|
if (dstNormal.Length > 0)
|
|
{
|
|
useNormal = vertexBuffer.Normals != null && dstNormal.Length == dstPosition.Length;
|
|
}
|
|
|
|
for (int i = 0; i < position.Length; ++i)
|
|
{
|
|
var j = joints[i];
|
|
var w = weights[i];
|
|
|
|
var sum = (w.x + w.y + w.z + w.w);
|
|
float factor;
|
|
if (sum > 0)
|
|
{
|
|
factor = 1.0f / sum;
|
|
}
|
|
else
|
|
{
|
|
factor = 1.0f;
|
|
j = new SkinJoints(m_indexOfRoot, 0, 0, 0);
|
|
w = new Vector4(1, 0, 0, 0);
|
|
}
|
|
if (j.Joint0 == ushort.MaxValue) w.x = 0;
|
|
if (j.Joint1 == ushort.MaxValue) w.y = 0;
|
|
if (j.Joint2 == ushort.MaxValue) w.z = 0;
|
|
if (j.Joint3 == ushort.MaxValue) w.w = 0;
|
|
|
|
{
|
|
var src = position[i]; // 位置ベクトル
|
|
var dst = Vector3.zero;
|
|
if (w.x > 0) dst += m_matrices[j.Joint0].MultiplyPoint(src) * w.x * factor;
|
|
if (w.y > 0) dst += m_matrices[j.Joint1].MultiplyPoint(src) * w.y * factor;
|
|
if (w.z > 0) dst += m_matrices[j.Joint2].MultiplyPoint(src) * w.z * factor;
|
|
if (w.w > 0) dst += m_matrices[j.Joint3].MultiplyPoint(src) * w.w * factor;
|
|
dstPosition[i] = new Vector3(dst.x, dst.y, dst.z);
|
|
}
|
|
if (useNormal)
|
|
{
|
|
var normalBuffer = vertexBuffer.Normals;
|
|
var normal = normalBuffer != null ? normalBuffer.Bytes.Reinterpret<Vector3>(1) : dstNormal;
|
|
var src = normal[i]; // 方向ベクトル
|
|
var dst = Vector3.zero;
|
|
if (w.x > 0) dst += m_matrices[j.Joint0].MultiplyVector(src) * w.x * factor;
|
|
if (w.y > 0) dst += m_matrices[j.Joint1].MultiplyVector(src) * w.y * factor;
|
|
if (w.z > 0) dst += m_matrices[j.Joint2].MultiplyVector(src) * w.z * factor;
|
|
if (w.w > 0) dst += m_matrices[j.Joint3].MultiplyVector(src) * w.w * factor;
|
|
dstNormal[i] = new Vector3(dst.x, dst.y, dst.z);
|
|
}
|
|
}
|
|
}
|
|
|
|
// だいたい Identity
|
|
static bool IsIdentity(Matrix4x4 m)
|
|
{
|
|
// 回転・スケール・しあー
|
|
if (
|
|
m.m00 == 1 && m.m10 == 0 && m.m20 == 0 && m.m30 == 0
|
|
&& m.m01 == 0 && m.m11 == 1 && m.m21 == 0 && m.m31 == 0
|
|
&& m.m02 == 0 && m.m12 == 0 && m.m22 == 1 && m.m32 == 0
|
|
&& m.m33 == 1
|
|
)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// translate
|
|
if (Math.Abs(m.m03) > 1e-5f) return false;
|
|
if (Math.Abs(m.m13) > 1e-5f) return false;
|
|
if (Math.Abs(m.m23) > 1e-5f) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
if (InverseMatrices != null)
|
|
{
|
|
var sb = new StringBuilder();
|
|
var matrices = InverseMatrices.Bytes.Reinterpret<Matrix4x4>(1);
|
|
var count = 0;
|
|
// var rootMatrix = Matrix4x4.identity;
|
|
// if (Root != null)
|
|
// {
|
|
// rootMatrix = Root.InverseMatrix;
|
|
// }
|
|
for (int i = 0; i < matrices.Length; ++i)
|
|
{
|
|
var m = matrices[i] * Joints[i].Matrix;
|
|
if (!IsIdentity(m))
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
if (count > 0)
|
|
{
|
|
sb.Append($"{count}/{Joints.Count} is not normalized");
|
|
}
|
|
else
|
|
{
|
|
sb.Append($"{Joints.Count} joints normalized");
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
else
|
|
{
|
|
return $"{Joints.Count} joints without InverseMatrices";
|
|
}
|
|
}
|
|
|
|
public void Replace(INativeArrayManager arrayManager, Node src, Node dst)
|
|
{
|
|
var removeIndex = Joints.IndexOf(src);
|
|
if (removeIndex >= 0)
|
|
{
|
|
Joints[removeIndex] = dst;
|
|
|
|
// エクスポート時に再計算させる
|
|
CalcInverseMatrices(arrayManager);
|
|
}
|
|
}
|
|
|
|
public void CalcInverseMatrices(INativeArrayManager arrayManager)
|
|
{
|
|
// var root = Root;
|
|
// if (root == null)
|
|
// {
|
|
// root = Joints[0].Ancestors().Last();
|
|
// }
|
|
// root.CalcWorldMatrix(Matrix4x4.identity, true);
|
|
|
|
// calc inverse bind matrices
|
|
var matricesBytes = arrayManager.CreateNativeArray<Byte>(Marshal.SizeOf(typeof(Matrix4x4)) * Joints.Count);
|
|
var matrices = matricesBytes.Reinterpret<Matrix4x4>(1);
|
|
for (int i = 0; i < Joints.Count; ++i)
|
|
{
|
|
// var w = Joints[i].Matrix;
|
|
// Matrix4x4.Invert(w, out Matrix4x4 inv);
|
|
if (Joints[i] != null)
|
|
{
|
|
matrices[i] = Joints[i].InverseMatrix;
|
|
}
|
|
}
|
|
InverseMatrices = new BufferAccessor(arrayManager, matricesBytes, AccessorValueType.FLOAT, AccessorVectorType.MAT4, Joints.Count);
|
|
}
|
|
|
|
static void Update(ref float weight, ref ushort index, int[] indexMap)
|
|
{
|
|
if (indexMap[index] == -1)
|
|
{
|
|
if (weight > 0)
|
|
{
|
|
throw new Exception();
|
|
}
|
|
//削除された
|
|
weight = 0;
|
|
index = 0;
|
|
}
|
|
else
|
|
{
|
|
// 参照を更新(変わっているかもしれない)
|
|
index = (ushort)indexMap[index];
|
|
}
|
|
}
|
|
}
|
|
}
|