// Magica Cloth.
// Copyright (c) MagicaSoft, 2020-2022.
// https://magicasoft.jp
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Jobs;
using UnityEngine.Profiling;
namespace MagicaCloth
{
///
/// ボーンデータ
///
public class PhysicsManagerBoneData : PhysicsManagerAccess
{
//=========================================================================================
///
/// ボーン制御フラグ
///
public const byte Flag_Reset = 0x01; // リセット
public const byte Flag_Restore = 0x10; // 復元許可フラグ
public const byte Flag_Write = 0x20; // 書き込み許可フラグ
///
/// 管理ボーンリスト
///
public FixedTransformAccessArray boneList;
///
/// ボーンフラグリスト
///
public FixedNativeList boneFlagList;
///
/// ボーンワールド位置リスト(※未来予測により補正される場合あり)
///
public FixedNativeList bonePosList;
///
/// ボーンワールド回転リスト(※未来予測により補正される場合あり)
///
public FixedNativeList boneRotList;
///
/// ボーンワールドスケールリスト(現在は初期化時に設定のみ不変)
///
public FixedNativeList boneSclList;
///
/// 親ボーンへのインデックス(-1=なし)
///
public FixedNativeList boneParentIndexList;
///
/// ボーンワールド位置リスト(オリジナル)
///
public FixedNativeList basePosList;
///
/// ボーンワールド回転リスト(オリジナル)
///
public FixedNativeList baseRotList;
///
/// ボーンがUnityPhysicsで動作するかの参照カウンタ(1以上で動作)
///
public FixedNativeList boneUnityPhysicsList;
///
/// ボーン未来予測位置リスト
///
public FixedNativeList futurePosList;
///
/// ボーン未来予測回転リスト
///
public FixedNativeList futureRotList;
//=========================================================================================
///
/// 復元ボーンリスト
///
public FixedTransformAccessArray restoreBoneList;
///
/// 復元ボーンの復元ローカル座標リスト
///
public FixedNativeList restoreBoneLocalPosList;
///
/// 復元ボーンの復元ローカル回転リスト
///
public FixedNativeList restoreBoneLocalRotList;
///
/// 復元ボーンの参照ボーンインデックス
///
public FixedNativeList restoreBoneIndexList;
//=========================================================================================
// ここはライトボーンごと
///
/// 書き込みボーンリスト
///
public FixedTransformAccessArray writeBoneList;
///
/// 書き込みボーンの参照ボーン姿勢インデックス(+1が入るので注意!)
///
public FixedNativeList writeBoneIndexList;
///
/// 書き込みボーンの対応するパーティクルインデックス
///
public ExNativeMultiHashMap writeBoneParticleIndexMap;
///
/// 読み込みボーンに対応する書き込みボーンのインデックス辞書
///
Dictionary boneToWriteIndexDict = new Dictionary();
///
/// 書き込みボーンの確定位置
/// 親がいる場合はローカル、いない場合はワールド格納
///
public FixedNativeList writeBonePosList;
///
/// 書き込みボーンの確定回転
/// 親がいる場合はローカル、いない場合はワールド格納
///
public FixedNativeList writeBoneRotList;
//=========================================================================================
///
/// ボーンリストに変化が合った場合にtrue
///
public bool hasBoneChanged { get; private set; }
///
/// プロファイラ用
///
private CustomSampler SamplerReadBoneScale { get; set; }
//=========================================================================================
///
/// 初期設定
///
public override void Create()
{
boneList = new FixedTransformAccessArray();
boneFlagList = new FixedNativeList();
bonePosList = new FixedNativeList();
boneRotList = new FixedNativeList();
boneSclList = new FixedNativeList();
boneParentIndexList = new FixedNativeList();
basePosList = new FixedNativeList();
baseRotList = new FixedNativeList();
boneUnityPhysicsList = new FixedNativeList();
futurePosList = new FixedNativeList();
futureRotList = new FixedNativeList();
restoreBoneList = new FixedTransformAccessArray();
restoreBoneLocalPosList = new FixedNativeList();
restoreBoneLocalRotList = new FixedNativeList();
restoreBoneIndexList = new FixedNativeList();
writeBoneList = new FixedTransformAccessArray();
writeBoneIndexList = new FixedNativeList();
writeBoneParticleIndexMap = new ExNativeMultiHashMap();
writeBonePosList = new FixedNativeList();
writeBoneRotList = new FixedNativeList();
// プロファイラ用
SamplerReadBoneScale = CustomSampler.Create("ReadBoneScale");
}
///
/// 破棄
///
public override void Dispose()
{
if (boneList == null)
return;
boneList.Dispose();
boneFlagList.Dispose();
bonePosList.Dispose();
boneRotList.Dispose();
boneSclList.Dispose();
boneParentIndexList.Dispose();
basePosList.Dispose();
baseRotList.Dispose();
boneUnityPhysicsList.Dispose();
futurePosList.Dispose();
futureRotList.Dispose();
restoreBoneList.Dispose();
restoreBoneLocalPosList.Dispose();
restoreBoneLocalRotList.Dispose();
restoreBoneIndexList.Dispose();
writeBoneList.Dispose();
writeBoneIndexList.Dispose();
writeBoneParticleIndexMap.Dispose();
writeBonePosList.Dispose();
writeBoneRotList.Dispose();
}
//=========================================================================================
///
/// 復元ボーン登録
///
///
///
///
///
public int AddRestoreBone(Transform target, float3 lpos, quaternion lrot, int boneIndex)
{
int restoreBoneIndex;
if (restoreBoneList.Exist(target))
{
// 参照カウンタ+
restoreBoneIndex = restoreBoneList.Add(target);
}
else
{
// 復元ローカル姿勢も登録
restoreBoneIndex = restoreBoneList.Add(target);
restoreBoneLocalPosList.Add(lpos);
restoreBoneLocalRotList.Add(lrot);
restoreBoneIndexList.Add(boneIndex);
hasBoneChanged = true;
}
return restoreBoneIndex;
}
///
/// 復元ボーン削除
///
///
public void RemoveRestoreBone(int restoreBoneIndex)
{
restoreBoneList.Remove(restoreBoneIndex);
if (restoreBoneList.Exist(restoreBoneIndex) == false)
{
// データも削除
restoreBoneLocalPosList.Remove(restoreBoneIndex);
restoreBoneLocalRotList.Remove(restoreBoneIndex);
restoreBoneIndexList.Remove(restoreBoneIndex);
hasBoneChanged = true;
}
}
///
/// ボーンの復元カウントを返す
///
public int RestoreBoneCount
{
get
{
return restoreBoneList.Count;
}
}
//=========================================================================================
///
/// 利用ボーン登録
///
///
///
/// 親ボーンのインデックス保持の有無
///
public int AddBone(Transform target, int pindex = -1, bool addParent = false)
{
int boneIndex;
if (boneList.Exist(target))
{
// 参照カウンタ+
boneIndex = boneList.Add(target);
// 親ボーンは後登録優先の上書き方式にする(v1.10.2)
if (addParent)
{
boneParentIndexList[boneIndex] = boneList.GetIndex(target.parent);
}
boneFlagList.Add(Flag_Reset); // 姿勢リセット
}
else
{
// 新規
var pos = float3.zero;
var rot = quaternion.identity;
boneIndex = boneList.Add(target);
boneFlagList.Add(Flag_Reset); // 姿勢リセット
bonePosList.Add(pos);
boneRotList.Add(rot);
boneSclList.Add(float3.zero);
if (addParent)
boneParentIndexList.Add(boneList.GetIndex(target.parent));
else
boneParentIndexList.Add(-1);
basePosList.Add(pos);
baseRotList.Add(rot);
boneUnityPhysicsList.Add(0);
futurePosList.Add(pos);
futureRotList.Add(rot);
hasBoneChanged = true;
}
//Debug.Log("AddBone:" + target.name + " index:" + boneIndex + " parent?:" + boneParentIndexList[boneIndex]);
// 書き込み設定
if (pindex >= 0)
{
if (boneToWriteIndexDict.ContainsKey(boneIndex))
{
Debug.LogWarning($"[{target.name}] is already registered as a write bone.");
}
else
{
//Debug.Log("AddWriteBone:" + target.name + " index:" + boneIndex + " parent?:" + boneParentIndexList[boneIndex]);
if (writeBoneList.Exist(target))
{
// 参照カウンタ+
writeBoneList.Add(target);
}
else
{
// 新規
writeBoneList.Add(target);
//Debug.Log("write bone index:" + boneIndex);
writeBoneIndexList.Add(boneIndex + 1); // +1を入れるので注意!
writeBonePosList.Add(float3.zero);
writeBoneRotList.Add(quaternion.identity);
hasBoneChanged = true;
}
int writeIndex = writeBoneList.GetIndex(target);
boneToWriteIndexDict.Add(boneIndex, writeIndex);
// 書き込み姿勢参照パーティクルインデックス登録
writeBoneParticleIndexMap.Add(writeIndex, pindex);
}
}
return boneIndex;
}
///
/// 利用ボーン解除
///
///
///
///
public bool RemoveBone(int boneIndex, int pindex = -1)
{
//Debug.Log("RemoveBone: index:" + boneIndex + " parent?:" + boneParentIndexList[boneIndex]);
bool del = false;
boneList.Remove(boneIndex);
if (boneList.Exist(boneIndex) == false)
{
// データも削除
boneFlagList.Remove(boneIndex);
bonePosList.Remove(boneIndex);
boneRotList.Remove(boneIndex);
boneSclList.Remove(boneIndex);
boneParentIndexList.Remove(boneIndex);
basePosList.Remove(boneIndex);
baseRotList.Remove(boneIndex);
boneUnityPhysicsList.Remove(boneIndex);
futurePosList.Remove(boneIndex);
futureRotList.Remove(boneIndex);
hasBoneChanged = true;
del = true;
}
// 書き込み設定から削除
if (pindex >= 0 && boneToWriteIndexDict.ContainsKey(boneIndex))
{
int writeIndex = boneToWriteIndexDict[boneIndex];
writeBoneList.Remove(writeIndex);
writeBoneIndexList.Remove(writeIndex);
writeBoneParticleIndexMap.Remove(writeIndex, pindex);
writeBonePosList.Remove(writeIndex);
writeBoneRotList.Remove(writeIndex);
hasBoneChanged = true;
if (writeBoneList.Exist(writeIndex) == false)
{
boneToWriteIndexDict.Remove(boneIndex);
//Debug.Log("RemoveWriteBone: index:" + boneIndex);
}
}
return del;
}
///
/// ボーンのUnityPhysics利用カウンタを増減させる
///
///
///
public void ChangeUnityPhysicsCount(int boneIndex, bool sw)
{
//Debug.Log($"Change Bone Physics Count [{boneIndex}]->{sw}");
boneUnityPhysicsList[boneIndex] += (short)(sw ? 1 : -1);
Debug.Assert(boneUnityPhysicsList[boneIndex] >= 0);
}
///
/// 未来予測をリセットする
///
///
public void ResetFuturePrediction(int boneIndex)
{
//Debug.Log($"ResetFuturePrediction:{boneIndex} F:{Time.frameCount}");
var flag = boneFlagList[boneIndex];
flag |= Flag_Reset;
boneFlagList[boneIndex] = flag;
}
///
/// 読み込みボーン数を返す
///
public int ReadBoneCount
{
get
{
return boneList.Count;
}
}
///
/// 書き込みボーン数を返す
///
public int WriteBoneCount
{
get
{
return writeBoneList.Count;
}
}
//=========================================================================================
///
/// ボーン情報のリセット
///
public void ResetBoneFromTransform(bool fixedUpdate)
{
// ボーン姿勢リセット
if (RestoreBoneCount > 0)
{
var job = new RestoreBoneJob()
{
fixedUpdate = fixedUpdate,
boneUnityPhysicsList = boneUnityPhysicsList.ToJobArray(),
boneFlagList = boneFlagList.ToJobArray(),
restoreBoneLocalPosList = restoreBoneLocalPosList.ToJobArray(),
restoreBoneLocalRotList = restoreBoneLocalRotList.ToJobArray(),
restoreBoneIndexList = restoreBoneIndexList.ToJobArray(),
};
Compute.MasterJob = job.Schedule(restoreBoneList.GetTransformAccessArray(), Compute.MasterJob);
}
}
///
/// ボーン姿勢の復元
///
[BurstCompile]
struct RestoreBoneJob : IJobParallelForTransform
{
public bool fixedUpdate;
[Unity.Collections.ReadOnly]
public NativeArray boneUnityPhysicsList;
[Unity.Collections.ReadOnly]
public NativeArray boneFlagList;
[Unity.Collections.ReadOnly]
public NativeArray restoreBoneLocalPosList;
[Unity.Collections.ReadOnly]
public NativeArray restoreBoneLocalRotList;
[Unity.Collections.ReadOnly]
public NativeArray restoreBoneIndexList;
// 復元ボーンごと
public void Execute(int index, TransformAccess transform)
{
var bindex = restoreBoneIndexList[index];
bool isUnityPhysics = boneUnityPhysicsList[bindex] > 0;
if (isUnityPhysics == fixedUpdate)
{
var flag = boneFlagList[bindex];
if ((flag & Flag_Restore) == 0)
return;
transform.localPosition = restoreBoneLocalPosList[index];
transform.localRotation = restoreBoneLocalRotList[index];
}
}
}
//=========================================================================================
///
/// ボーン情報の読み込み
///
public void ReadBoneFromTransform()
{
// ボーン姿勢読み込み
if (ReadBoneCount > 0)
{
var updateTime = manager.UpdateTime;
// 未来予測補間率
float futureRate = updateTime.IsDelay ? updateTime.FuturePredictionRate : 0.0f;
// 未来予測が不要ならば従来どおり
if (futureRate < 0.01f)
{
// ボーンから姿勢読み込み(ルートが別れていないとジョブが並列化できないので注意!)
var job = new ReadBoneJob0()
{
fixedUpdateCount = updateTime.FixedUpdateCount,
bonePosList = bonePosList.ToJobArray(),
boneRotList = boneRotList.ToJobArray(),
boneSclList = boneSclList.ToJobArray(),
basePosList = basePosList.ToJobArray(),
baseRotList = baseRotList.ToJobArray(),
futurePosList = futurePosList.ToJobArray(),
futureRotList = futureRotList.ToJobArray(),
boneUnityPhysicsList = boneUnityPhysicsList.ToJobArray(),
boneFlagList = boneFlagList.ToJobArray(),
};
Compute.MasterJob = job.Schedule(boneList.GetTransformAccessArray(), Compute.MasterJob);
}
else
{
// 未来予測あり
// Update更新での未来予測補間率を求める
float normalFutureRatio = updateTime.DeltaTime > Define.Compute.Epsilon ?
math.clamp((updateTime.AverageDeltaTime / updateTime.DeltaTime) * futureRate, 0.0f, 2.0f) : 0.0f;
// FixedUpdate更新での未来予測補間率を求める
float fixedFutureRatio = updateTime.FixedUpdateCount > 0 ? (1.0f / updateTime.FixedUpdateCount) * futureRate : 0.0f;
#if true
// 次に予想されるフレーム時間を加算することにより実行されるFixedUpdateの回数を予測する
float fixedNextTime = Time.time + Time.smoothDeltaTime;
float fixedInterval = fixedNextTime - Time.fixedTime;
int nextFixedCount = math.max((int)(fixedInterval / Time.fixedDeltaTime), 1);
fixedFutureRatio *= nextFixedCount;
#endif
//Debug.Log($"normalFutureRatio = {normalFutureRatio}");
// ボーンから姿勢読み込み(ルートが別れていないとジョブが並列化できないので注意!)
var job = new ReadBoneJob1()
{
fixedUpdateCount = updateTime.FixedUpdateCount,
normalFutureRatio = normalFutureRatio,
fixedFutureRatio = fixedFutureRatio,
normalDeltaTime = Time.smoothDeltaTime,
fixedDeltaTime = Time.fixedDeltaTime,
bonePosList = bonePosList.ToJobArray(),
boneRotList = boneRotList.ToJobArray(),
boneSclList = boneSclList.ToJobArray(),
basePosList = basePosList.ToJobArray(),
baseRotList = baseRotList.ToJobArray(),
boneUnityPhysicsList = boneUnityPhysicsList.ToJobArray(),
futurePosList = futurePosList.ToJobArray(),
futureRotList = futureRotList.ToJobArray(),
boneFlagList = boneFlagList.ToJobArray(),
};
Compute.MasterJob = job.Schedule(boneList.GetTransformAccessArray(), Compute.MasterJob);
}
}
}
///
/// ボーン姿勢の読込み(未来予測なし)
///
[BurstCompile]
struct ReadBoneJob0 : IJobParallelForTransform
{
public int fixedUpdateCount;
[Unity.Collections.WriteOnly]
public NativeArray bonePosList;
[Unity.Collections.WriteOnly]
public NativeArray boneRotList;
[Unity.Collections.WriteOnly]
public NativeArray boneSclList;
//[Unity.Collections.WriteOnly]
public NativeArray basePosList;
//[Unity.Collections.WriteOnly]
public NativeArray baseRotList;
[Unity.Collections.WriteOnly]
public NativeArray futurePosList;
[Unity.Collections.WriteOnly]
public NativeArray futureRotList;
[Unity.Collections.ReadOnly]
public NativeArray boneUnityPhysicsList;
public NativeArray boneFlagList;
// 読み込みボーンごと
public void Execute(int index, TransformAccess transform)
{
// UnityPhysicsモードで今回更新が無い場合は前回のTransfrom姿勢をボーン姿勢に設定して終了する
bool unityPhysics = boneUnityPhysicsList[index] > 0;
var flag = boneFlagList[index];
bool reset = (flag & Flag_Reset) != 0;
if (unityPhysics == false || fixedUpdateCount > 0 || reset)
{
// 通常更新
// UnityPhysics更新かつフレーム更新ありの場合
// ボーンリセット時
float3 pos = transform.position;
quaternion rot = transform.rotation;
bonePosList[index] = pos;
boneRotList[index] = rot;
basePosList[index] = pos;
baseRotList[index] = rot;
futurePosList[index] = pos;
futureRotList[index] = rot;
// lossyScale取得(現在はUnity2019.2.14以上のみ)
// マトリックスから正確なスケール値を算出する(これはTransform.lossyScaleと等価)
float4x4 m = transform.localToWorldMatrix;
var irot = math.inverse(rot);
var m2 = math.mul(new float4x4(irot, float3.zero), m);
var scl = new float3(m2.c0.x, m2.c1.y, m2.c2.z);
boneSclList[index] = scl;
}
else
{
// UnityPhysics更新かつフレーム更新なしの場合
bonePosList[index] = basePosList[index];
boneRotList[index] = baseRotList[index];
}
// リセットフラグクリア
if (reset && (unityPhysics == false || fixedUpdateCount > 0))
{
flag = (byte)(flag & ~Flag_Reset);
boneFlagList[index] = flag;
}
}
}
///
/// ボーン姿勢の読込み(未来予測あり)
///
[BurstCompile]
struct ReadBoneJob1 : IJobParallelForTransform
{
public int fixedUpdateCount;
public float normalFutureRatio;
public float fixedFutureRatio;
public float normalDeltaTime;
public float fixedDeltaTime;
[Unity.Collections.WriteOnly]
public NativeArray bonePosList;
[Unity.Collections.WriteOnly]
public NativeArray boneRotList;
[Unity.Collections.WriteOnly]
public NativeArray boneSclList;
public NativeArray basePosList;
public NativeArray baseRotList;
[Unity.Collections.ReadOnly]
public NativeArray boneUnityPhysicsList;
public NativeArray futurePosList;
public NativeArray futureRotList;
public NativeArray boneFlagList;
// 読み込みボーンごと
public void Execute(int index, TransformAccess transform)
{
bool unityPhysics = boneUnityPhysicsList[index] > 0;
var flag = boneFlagList[index];
bool reset = (flag & Flag_Reset) != 0;
if (unityPhysics == false || fixedUpdateCount > 0 || reset)
{
// 通常更新
// UnityPhysics更新かつフレーム更新ありの場合
// ボーンリセット時
float3 pos = transform.position;
quaternion rot = transform.rotation;
if (reset)
{
// リセット
//Debug.Log($"reset bone :{index}");
basePosList[index] = pos;
baseRotList[index] = rot;
bonePosList[index] = pos;
boneRotList[index] = rot;
futurePosList[index] = pos;
futureRotList[index] = rot;
}
else
{
// 更新:未来予測
//Debug.Log($"read bone :{index}");
var oldPos = basePosList[index];
var oldRot = baseRotList[index];
basePosList[index] = pos;
baseRotList[index] = rot;
// 速度制限(v1.11.1)
float moveRatio = 0;
float angRatio = 0;
float deltaLength = math.distance(oldPos, pos);
float deltaAngle = math.degrees(math.abs(MathUtility.Angle(oldRot, rot)));
float dtime = unityPhysics ? fixedDeltaTime : normalDeltaTime;
if (dtime > Define.Compute.Epsilon)
{
float moveSpeed = deltaLength / dtime;
float angSpeed = deltaAngle / dtime;
//if (deltaLength > 1e-06f)
// Debug.Log($"read bone :{index}, movesp:{moveSpeed}, angsp:{angSpeed}");
const float maxMoveSpeed = 1.0f;
moveRatio = moveSpeed > maxMoveSpeed ? maxMoveSpeed / moveSpeed : 1.0f;
const float maxAngleSpeed = 360.0f; // deg
angRatio = angSpeed > maxAngleSpeed ? maxAngleSpeed / angSpeed : 1.0f;
}
// 未来予測
float ratio = unityPhysics ? fixedFutureRatio : normalFutureRatio; // ボーンの更新モードにより変化
pos = math.lerp(oldPos, pos, 1.0f + ratio * moveRatio);
rot = math.slerp(oldRot, rot, 1.0f + ratio * angRatio);
rot = math.normalize(rot);
bonePosList[index] = pos;
boneRotList[index] = rot;
// 未来予測姿勢を記録しておく
futurePosList[index] = pos;
futureRotList[index] = rot;
}
// lossyScale取得(現在はUnity2019.2.14以上のみ)
// マトリックスから正確なスケール値を算出する(これはTransform.lossyScaleと等価)
float4x4 m = transform.localToWorldMatrix;
var irot = math.inverse(rot);
var m2 = math.mul(new float4x4(irot, float3.zero), m);
var scl = new float3(m2.c0.x, m2.c1.y, m2.c2.z);
boneSclList[index] = scl;
}
else
{
// UnityPhysics更新かつフレーム更新なしの場合
// 前回計算した未来予測姿勢を返す
bonePosList[index] = futurePosList[index];
boneRotList[index] = futureRotList[index];
}
// リセットフラグクリア
if (reset && (unityPhysics == false || fixedUpdateCount > 0))
{
flag = (byte)(flag & ~Flag_Reset);
boneFlagList[index] = flag;
}
}
}
//=========================================================================================
///
/// 書き込みボーン姿勢をローカル姿勢に変換する
///
public void ConvertWorldToLocal()
{
if (WriteBoneCount > 0)
{
var job = new ConvertWorldToLocalJob()
{
writeBoneIndexList = writeBoneIndexList.ToJobArray(),
boneFlagList = boneFlagList.ToJobArray(),
bonePosList = bonePosList.ToJobArray(),
boneRotList = boneRotList.ToJobArray(),
boneSclList = boneSclList.ToJobArray(),
boneParentIndexList = boneParentIndexList.ToJobArray(),
writeBonePosList = writeBonePosList.ToJobArray(),
writeBoneRotList = writeBoneRotList.ToJobArray(),
};
Compute.MasterJob = job.Schedule(writeBoneIndexList.Length, 16, Compute.MasterJob);
}
}
///
/// ボーン姿勢をローカル姿勢に変換する
///
[BurstCompile]
struct ConvertWorldToLocalJob : IJobParallelFor
{
[Unity.Collections.ReadOnly]
public NativeArray writeBoneIndexList;
[Unity.Collections.ReadOnly]
public NativeArray boneFlagList;
[Unity.Collections.ReadOnly]
public NativeArray bonePosList;
[Unity.Collections.ReadOnly]
public NativeArray boneRotList;
[Unity.Collections.ReadOnly]
public NativeArray boneSclList;
[Unity.Collections.ReadOnly]
public NativeArray boneParentIndexList;
[Unity.Collections.WriteOnly]
public NativeArray writeBonePosList;
[Unity.Collections.WriteOnly]
public NativeArray writeBoneRotList;
// 書き込みボーンごと
public void Execute(int index)
{
int bindex = writeBoneIndexList[index];
if (bindex == 0)
return;
bindex--; // +1が入っているので-1する
// 書き込みフラグチェック
var flag = boneFlagList[bindex];
if ((flag & Flag_Write) == 0)
return;
var pos = bonePosList[bindex];
var rot = boneRotList[bindex];
int parentIndex = boneParentIndexList[bindex];
if (parentIndex >= 0)
{
// 親がいる場合はローカル座標で書き込む
var ppos = bonePosList[parentIndex];
var prot = boneRotList[parentIndex];
var pscl = boneSclList[parentIndex];
var iprot = math.inverse(prot);
var v = pos - ppos;
var lpos = math.mul(iprot, v);
lpos /= pscl;
var lrot = math.mul(iprot, rot);
// マイナススケール対応
if (pscl.x < 0 || pscl.y < 0 || pscl.z < 0)
lrot = new quaternion(lrot.value * new float4(-math.sign(pscl), 1));
writeBonePosList[index] = lpos;
writeBoneRotList[index] = lrot;
}
else
{
// 親がいない場合はワールド座標で書き込む
writeBonePosList[index] = pos;
writeBoneRotList[index] = rot;
}
}
}
//=========================================================================================
///
/// ボーン姿勢をトランスフォームに書き込む
///
public void WriteBoneToTransform(int bufferIndex)
{
if (WriteBoneCount > 0)
{
var job = new WriteBontToTransformJob2()
{
fixedUpdateCount = manager.UpdateTime.FixedUpdateCount,
boneFlagList = boneFlagList.ToJobArray(bufferIndex),
writeBoneIndexList = writeBoneIndexList.ToJobArray(bufferIndex),
boneParentIndexList = boneParentIndexList.ToJobArray(),
writeBonePosList = writeBonePosList.ToJobArray(bufferIndex),
writeBoneRotList = writeBoneRotList.ToJobArray(bufferIndex),
boneUnityPhysicsList = boneUnityPhysicsList.ToJobArray(),
};
Compute.MasterJob = job.Schedule(writeBoneList.GetTransformAccessArray(), Compute.MasterJob);
}
}
///
/// ボーン姿勢をトランスフォームに書き込む
///
[BurstCompile]
struct WriteBontToTransformJob2 : IJobParallelForTransform
{
public int fixedUpdateCount;
[Unity.Collections.ReadOnly]
public NativeArray boneFlagList;
[Unity.Collections.ReadOnly]
public NativeArray writeBoneIndexList;
[Unity.Collections.ReadOnly]
public NativeArray boneParentIndexList;
[Unity.Collections.ReadOnly]
public NativeArray writeBonePosList;
[Unity.Collections.ReadOnly]
public NativeArray writeBoneRotList;
[Unity.Collections.ReadOnly]
public NativeArray boneUnityPhysicsList;
// 書き込みトランスフォームごと
public void Execute(int index, TransformAccess transform)
{
if (index >= writeBoneIndexList.Length)
return;
int bindex = writeBoneIndexList[index];
if (bindex == 0)
return;
bindex--; // +1が入っているので-1する
// 書き込みフラグチェック
var flag = boneFlagList[bindex];
if ((flag & Flag_Write) == 0)
return;
bool unityPhysics = boneUnityPhysicsList[bindex] > 0;
if (unityPhysics == false || fixedUpdateCount > 0)
{
var pos = writeBonePosList[index];
var rot = writeBoneRotList[index];
int parentIndex = boneParentIndexList[bindex];
//Debug.Log($"Write Bone:{bindex} Parent:{parentIndex} Pos:{pos}");
if (parentIndex >= 0)
{
// 親を参照する場合はローカル座標で書き込む
transform.localPosition = pos;
transform.localRotation = rot;
}
else
{
// 親がいない場合はワールドで書き込む
transform.position = pos;
transform.rotation = rot;
}
}
}
}
//=========================================================================================
///
/// ボーン情報を書き込みバッファにコピーする
/// これは遅延実行時のみ
///
public void CopyBoneBuffer()
{
var job0 = new CopyBoneJob0()
{
bonePosList = writeBonePosList.ToJobArray(),
boneRotList = writeBoneRotList.ToJobArray(),
backBonePosList = writeBonePosList.ToJobArray(1),
backBoneRotList = writeBoneRotList.ToJobArray(1),
};
var jobHandle0 = job0.Schedule(writeBonePosList.Length, 16);
var job1 = new CopyBoneJob1()
{
writeBoneIndexList = writeBoneIndexList.ToJobArray(),
backWriteBoneIndexList = writeBoneIndexList.ToJobArray(1),
};
var jobHandle1 = job1.Schedule(writeBoneIndexList.Length, 16);
var job2 = new CopyBoneJob2()
{
boneFlagList = boneFlagList.ToJobArray(),
backBoneFlagList = boneFlagList.ToJobArray(1),
};
var jobHandle2 = job2.Schedule(boneFlagList.Length, 16);
Compute.MasterJob = JobHandle.CombineDependencies(jobHandle0, jobHandle1, jobHandle2);
}
[BurstCompile]
struct CopyBoneJob0 : IJobParallelFor
{
[Unity.Collections.ReadOnly]
public NativeArray bonePosList;
[Unity.Collections.ReadOnly]
public NativeArray boneRotList;
[Unity.Collections.WriteOnly]
public NativeArray backBonePosList;
[Unity.Collections.WriteOnly]
public NativeArray backBoneRotList;
public void Execute(int index)
{
backBonePosList[index] = bonePosList[index];
backBoneRotList[index] = boneRotList[index];
}
}
[BurstCompile]
struct CopyBoneJob1 : IJobParallelFor
{
[Unity.Collections.ReadOnly]
public NativeArray writeBoneIndexList;
[Unity.Collections.WriteOnly]
public NativeArray backWriteBoneIndexList;
public void Execute(int index)
{
backWriteBoneIndexList[index] = writeBoneIndexList[index];
}
}
[BurstCompile]
struct CopyBoneJob2 : IJobParallelFor
{
[Unity.Collections.ReadOnly]
public NativeArray boneFlagList;
[Unity.Collections.WriteOnly]
public NativeArray backBoneFlagList;
public void Execute(int index)
{
backBoneFlagList[index] = boneFlagList[index];
}
}
}
}