// Magica Cloth.
// Copyright (c) MagicaSoft, 2020-2022.
// https://magicasoft.jp
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace MagicaCloth
{
///
/// ボーンクロス
///
[HelpURL("https://magicasoft.jp/magica-cloth-bone-cloth/")]
[AddComponentMenu("MagicaCloth/MagicaBoneCloth")]
public class MagicaBoneCloth : BaseCloth
{
///
/// データバージョン
///
private const int DATA_VERSION = 7;
///
/// エラーデータバージョン
///
private const int ERR_DATA_VERSION = 3;
///
/// メッシュデータ
///
[SerializeField]
private MeshData meshData = null;
[SerializeField]
private int meshDataHash;
[SerializeField]
private int meshDataVersion;
///
/// 使用ルートトランスフォーム情報
///
[SerializeField]
private BoneClothTarget clothTarget = new BoneClothTarget();
///
/// 最終的に使用されるすべてのトランスフォーム情報
///
[SerializeField]
private List useTransformList = new List();
[SerializeField]
private List useTransformPositionList = new List();
[SerializeField]
private List useTransformRotationList = new List();
[SerializeField]
private List useTransformScaleList = new List();
//=========================================================================================
public override ComponentType GetComponentType()
{
return ComponentType.BoneCloth;
}
//=========================================================================================
///
/// データハッシュを求める
///
///
public override int GetDataHash()
{
int hash = base.GetDataHash();
hash += MeshData.GetDataHash();
hash += clothTarget.GetDataHash();
hash += useTransformList.GetDataHash();
hash += useTransformPositionList.GetDataHash();
hash += useTransformRotationList.GetDataHash();
hash += useTransformScaleList.GetDataHash();
return hash;
}
//=========================================================================================
public BoneClothTarget ClothTarget
{
get
{
return clothTarget;
}
}
public MeshData MeshData
{
get
{
#if UNITY_EDITOR
if (Application.isPlaying)
return meshData;
else
{
// unity2019.3で参照がnullとなる不具合の対処(臨時)
var so = new SerializedObject(this);
return so.FindProperty("meshData").objectReferenceValue as MeshData;
}
#else
return meshData;
#endif
}
}
//=========================================================================================
protected override void Reset()
{
base.Reset();
ResetParams();
}
protected override void OnValidate()
{
base.OnValidate();
}
protected override void ClothInit()
{
// ルートトランスフォームの親をすべて登録する
ClothTarget.AddParentTransform();
base.ClothInit();
}
protected override void ClothDispose()
{
// ルートトランスフォームの親をすべて解除する
ClothTarget.RemoveParentTransform();
base.ClothDispose();
}
protected override void ClothActive()
{
base.ClothActive();
}
///
/// 頂点ごとのパーティクルフラグ設定
///
///
///
protected override uint UserFlag(int index)
{
uint flag = 0;
bool isFix = ClothData.IsFixedVertex(index);
flag |= PhysicsManagerParticleData.Flag_Transform_Restore; // 実行前にlocalPos/localRot復元
flag |= isFix ? (PhysicsManagerParticleData.Flag_Transform_Read_Pos | PhysicsManagerParticleData.Flag_Transform_Read_Rot) : 0; // トランスフォームをpos/rotに読み込み(固定のみ)
flag |= PhysicsManagerParticleData.Flag_Transform_Read_Base; // トランスフォームをbasePos/baseRotに読み込み
flag |= PhysicsManagerParticleData.Flag_Transform_Write; // 最後にトランスフォームへ座標書き込み
flag |= PhysicsManagerParticleData.Flag_Transform_Parent; // 親トランスフォームを参照する
return flag;
}
///
/// 頂点ごとの連動トランスフォーム設定
///
///
///
protected override Transform UserTransform(int index)
{
return GetUseTransform(index);
}
///
/// 頂点ごとの連動トランスフォームのLocalPositionを返す(不要な場合は0)
///
///
///
protected override float3 UserTransformLocalPosition(int vindex)
{
int index = ClothData.useVertexList[vindex];
return useTransformPositionList[index];
}
///
/// 頂点ごとの連動トランスフォームのLocalRotationを返す(不要な場合はquaternion.identity)
///
///
///
protected override quaternion UserTransformLocalRotation(int vindex)
{
int index = ClothData.useVertexList[vindex];
return useTransformRotationList[index];
}
///
/// デフォーマーを返す
///
///
///
public override BaseMeshDeformer GetDeformer()
{
// BoneClothには不要
return null;
}
///
/// クロス初期化時に必要なMeshDataを返す(不要ならnull)
///
///
protected override MeshData GetMeshData()
{
return MeshData;
}
///
/// クロス初期化の主にワーカーへの登録
///
protected override void WorkerInit()
{
// BoneClothには不要
}
///
/// デフォーマーが必須か返す
///
///
public override bool IsRequiresDeformer()
{
// BoneClothには不要
return false;
}
///
/// デフォーマーごとの使用頂点設定
/// 使用頂点に対して AddUseVertex() / RemoveUseVertex() を実行する
///
///
///
protected override void SetDeformerUseVertex(bool sw, BaseMeshDeformer deformer)
{
// BoneClothには不要
}
///
/// UnityPhyiscsでの更新の変更
/// 継承クラスは自身の使用するボーンの状態更新などを記述する
///
///
protected override void ChangeUseUnityPhysics(bool sw)
{
base.ChangeUseUnityPhysics(sw);
if (teamId <= 0)
return;
ClothTarget.ChangeUnityPhysicsCount(sw);
}
protected override void OnChangeCalculation()
{
base.OnChangeCalculation();
if (IsCalculate)
{
// 遅延実行時のみ
if (MagicaPhysicsManager.Instance.IsDelay)
{
// ルートトランスフォームの親の未来予測をリセットする
ClothTarget.ResetFuturePredictionParentTransform();
// 読み込みボーンの未来予測をリセットする
MagicaPhysicsManager.Instance.Particle.ResetFuturePredictionTransform(particleChunk);
}
}
// 書き込みボーンのフラグ設定
if (MagicaPhysicsManager.IsInstance())
{
//Debug.Log($"ChangeWriteBoneFlag:[{this.name}] CullMode:{CullingMode} Calc:{IsCalculate} F:{Time.frameCount}");
MagicaPhysicsManager.Instance.Team.ChangeBoneFlag(TeamId, CullingMode, IsCalculate);
}
}
//=========================================================================================
///
/// 使用するトランスフォームをリストにして返す
///
///
public List GetTransformList()
{
HashSet tset = new HashSet();
int cnt = clothTarget.RootCount;
for (int i = 0; i < cnt; i++)
{
var root = clothTarget.GetRoot(i);
if (root != null)
{
var tlist = root.GetComponentsInChildren();
foreach (var t in tlist)
tset.Add(t);
}
}
List transformList = new List();
foreach (var t in tset)
{
transformList.Add(t);
}
return transformList;
}
Transform GetUseTransform(int index)
{
int vindex = ClothData.useVertexList[index];
return useTransformList[vindex];
}
int UseTransformCount
{
get
{
return useTransformList.Count;
}
}
///
/// 使用するトランスフォームを登録順にグリッドにして返す
///
///
///
public List>> GetTransformGrid(out int maxLevel)
{
maxLevel = 0;
List>> grid = new List>>();
int cnt = clothTarget.RootCount;
for (int i = 0; i < cnt; i++)
{
List> line = new List>();
var root = clothTarget.GetRoot(i);
if (root != null)
{
var tq = new Queue();
var lq = new Queue();
tq.Enqueue(root);
lq.Enqueue(0);
while (tq.Count > 0)
{
var t = tq.Dequeue();
int lv = lq.Dequeue();
if (line.Count <= lv)
{
line.Add(new List());
}
line[lv].Add(t);
maxLevel = Mathf.Max(maxLevel, lv + 1);
// chile
for (int j = 0; j < t.childCount; j++)
{
var c = t.GetChild(j);
tq.Enqueue(c);
lq.Enqueue(lv + 1);
}
}
}
grid.Add(line);
}
return grid;
}
//=========================================================================================
public override int GetVersion()
{
return DATA_VERSION;
}
///
/// エラーとするデータバージョンを取得する
///
///
public override int GetErrorVersion()
{
return ERR_DATA_VERSION;
}
///
/// データを検証して結果を格納する
///
///
public override void CreateVerifyData()
{
base.CreateVerifyData();
meshDataHash = MeshData.SaveDataHash;
meshDataVersion = MeshData.SaveDataVersion;
}
///
/// 現在のデータが正常(実行できる状態)か返す
///
///
public override Define.Error VerifyData()
{
var baseError = base.VerifyData();
if (baseError != Define.Error.None)
return baseError;
if (ClothData == null)
return Define.Error.ClothDataNull;
if (MeshData == null)
return Define.Error.MeshDataNull;
var meshDataError = MeshData.VerifyData();
if (meshDataError != Define.Error.None)
return meshDataError;
if (meshDataHash != MeshData.SaveDataHash)
return Define.Error.MeshDataHashMismatch;
if (meshDataVersion != MeshData.SaveDataVersion)
return Define.Error.MeshDataVersionMismatch;
if (useTransformList.Count == 0)
return Define.Error.UseTransformCountZero;
if (UseTransformCount != MeshData.VertexCount)
return Define.Error.UseTransformCountMismatch;
foreach (var t in useTransformList)
if (t == null)
return Define.Error.UseTransformNull;
return Define.Error.None;
}
///
/// データ検証の結果テキストを取得する
///
///
public override string GetInformation()
{
StaticStringBuilder.Clear();
var err = VerifyData();
if (err == Define.Error.None)
{
// OK
var cdata = ClothData;
StaticStringBuilder.AppendLine("Active: ", Status.IsActive);
StaticStringBuilder.AppendLine($"Visible: {IsVisible}");
StaticStringBuilder.AppendLine($"Calculation:{IsCalculate}");
StaticStringBuilder.AppendLine("Transform: ", MeshData.VertexCount);
StaticStringBuilder.AppendLine("Line: ", MeshData.LineCount);
StaticStringBuilder.AppendLine("Triangle: ", MeshData.TriangleCount);
StaticStringBuilder.AppendLine("Clamp Distance: ", cdata.ClampDistanceConstraintCount);
StaticStringBuilder.AppendLine("Clamp Position: ", clothParams.UseClampPositionLength ? cdata.VertexUseCount : 0);
StaticStringBuilder.AppendLine("Clamp Rotation [", cdata.clampRotationAlgorithm, "] : ", cdata.GetClampRotationCount());
StaticStringBuilder.AppendLine("Struct Distance: ", cdata.StructDistanceConstraintCount / 2);
StaticStringBuilder.AppendLine("Bend Distance: ", cdata.BendDistanceConstraintCount / 2);
StaticStringBuilder.AppendLine("Near Distance: ", cdata.NearDistanceConstraintCount / 2);
StaticStringBuilder.AppendLine("Restore Rotation [", cdata.restoreRotationAlgorithm, "] : ", cdata.GetRestoreRotationCount());
StaticStringBuilder.AppendLine("Triangle Bend [", cdata.triangleBendAlgorithm, "] : ", cdata.TriangleBendConstraintCount);
//StaticStringBuilder.AppendLine("Rotation Interpolation: ", " 0 ? "ON" : "OFF", "> 0 ? "ON" : "OFF", ">");
StaticStringBuilder.Append("Rotation Interpolation: ");
if (cdata.LineRotationWorkerCount > 0)
StaticStringBuilder.Append("Line ");
if (cdata.TriangleRotationWorkerCount > 0)
StaticStringBuilder.Append("Triangle ");
StaticStringBuilder.AppendLine();
StaticStringBuilder.Append("Collider: ", teamData.ColliderCount);
}
else if (err == Define.Error.EmptyData)
{
StaticStringBuilder.Append(Define.GetErrorMessage(err));
}
else
{
// エラー
StaticStringBuilder.AppendLine("This bone cloth is in a state error!");
if (Application.isPlaying)
{
StaticStringBuilder.AppendLine("Execution stopped.");
}
else
{
StaticStringBuilder.AppendLine("Please recreate the bone cloth data.");
}
StaticStringBuilder.Append(Define.GetErrorMessage(err));
}
return StaticStringBuilder.ToString();
}
//=========================================================================================
///
/// ボーンを置換する
///
///
public override void ReplaceBone(Dictionary boneReplaceDict)
{
base.ReplaceBone(boneReplaceDict);
for (int i = 0; i < useTransformList.Count; i++)
{
useTransformList[i] = MeshUtility.GetReplaceBone(useTransformList[i], boneReplaceDict);
}
clothTarget.ReplaceBone(boneReplaceDict);
}
///
/// 現在使用しているボーンを格納して返す
///
///
public override HashSet GetUsedBones()
{
var bones = base.GetUsedBones();
bones.UnionWith(useTransformList);
bones.UnionWith(clothTarget.GetUsedBones());
return bones;
}
//=========================================================================================
///
/// メッシュのワールド座標/法線/接線を返す(エディタ用)
///
///
///
///
/// 頂点数
public override int GetEditorPositionNormalTangent(out List wposList, out List wnorList, out List wtanList)
{
wposList = new List();
wnorList = new List();
wtanList = new List();
var tlist = GetTransformList();
foreach (var t in tlist)
{
wposList.Add(t.position);
wnorList.Add(t.TransformDirection(Vector3.forward));
var up = t.TransformDirection(Vector3.up);
wtanList.Add(up);
}
return wposList.Count;
}
///
/// メッシュのトライアングルリストを返す(エディタ用)
///
///
public override List GetEditorTriangleList()
{
List triangles = new List();
var mdata = MeshData;
if (mdata != null && mdata.triangleList != null)
triangles = new List(mdata.triangleList);
return triangles;
}
///
/// メッシュのラインリストを返す(エディタ用)
///
///
public override List GetEditorLineList()
{
List lines = new List();
var mdata = MeshData;
if (mdata != null && mdata.lineList != null)
lines = new List(mdata.lineList);
return lines;
}
//=========================================================================================
///
/// 頂点の選択状態をリストにして返す(エディタ用)
/// 選択状態は ClothSelection.Invalid / ClothSelection.Fixed / ClothSelection.Move
/// すべてがInvalidならばnullを返す
///
///
public override List GetSelectionList()
{
if (ClothSelection != null && MeshData != null)
return ClothSelection.GetSelectionData(MeshData, null);
else
return null;
}
///
/// 頂点の使用状態をリストにして返す(エディタ用)
/// 数値が1以上ならば使用中とみなす
/// すべて使用状態ならばnullを返す
///
///
public override List GetUseList()
{
// 不要かも
//if (Application.isPlaying)
//{
// if (ClothData != null)
// {
// var useList = new List();
// foreach (var sel in ClothData.selectionData)
// useList.Add(sel != SelectionData.Invalid ? 1 : 0);
// return useList;
// }
//}
return null;
}
//=========================================================================================
///
/// 共有データオブジェクト収集
///
///
public override List GetAllShareDataObject()
{
var sdata = base.GetAllShareDataObject();
sdata.Add(MeshData);
return sdata;
}
///
/// sourceの共有データを複製して再セットする
/// 再セットした共有データを返す
///
///
///
public override ShareDataObject DuplicateShareDataObject(ShareDataObject source)
{
var sdata = base.DuplicateShareDataObject(source);
if (sdata != null)
return sdata;
if (MeshData == source)
{
//meshData = Instantiate(MeshData);
meshData = ShareDataObject.Clone(MeshData);
return meshData;
}
return null;
}
//=========================================================================================
///
/// パラメータ初期化
///
void ResetParams()
{
clothParams.AlgorithmType = ClothParams.Algorithm.Algorithm_2;
clothParams.SetRadius(0.05f, 0.05f);
clothParams.SetMass(10.0f, 1.0f, true, -0.5f, true);
clothParams.SetGravity(true, -5.0f, -5.0f);
clothParams.SetDrag(true, 0.01f, 0.01f);
clothParams.SetMaxVelocity(true, 3.0f, 3.0f);
clothParams.SetWorldInfluence(3.0f, 0.5f, 1.0f);
clothParams.SetTeleport(false);
clothParams.SetClampDistanceRatio(true, 0.7f, 1.05f, 0.1f);
clothParams.SetClampPositionLength(false, 0.0f, 0.4f);
clothParams.SetClampRotationAngle(false, 0.0f, 180.0f, 0.2f);
clothParams.SetRestoreDistance(1.0f);
clothParams.SetRestoreRotation(false, 0.03f, 0.005f, 0.3f);
clothParams.SetSpring(false);
clothParams.SetAdjustRotation();
clothParams.SetTriangleBend(true, 0.7f, 0.7f);
clothParams.SetVolume(false);
clothParams.SetCollision(false, 0.1f, 0.03f);
clothParams.SetExternalForce(0.3f, 1.0f, 0.7f, 0.6f);
}
}
}