// 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-spring/")]
[AddComponentMenu("MagicaCloth/MagicaBoneSpring")]
public class MagicaBoneSpring : 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.BoneSpring;
}
//=========================================================================================
///
/// データハッシュを求める
///
///
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();
// スプリングではClampPositonの速度制限は無視する
MagicaPhysicsManager.Instance.Team.SetFlag(TeamId, PhysicsManagerTeamData.Flag_IgnoreClampPositionVelocity, true);
}
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 bool IsRequiresDeformer()
{
// BoneClothには不要
return false;
}
///
/// デフォーマーを返す
///
///
///
public override BaseMeshDeformer GetDeformer()
{
// BoneClothには不要
return null;
}
///
/// クロス初期化時に必要なMeshDataを返す(不要ならnull)
///
///
protected override MeshData GetMeshData()
{
return MeshData;
}
///
/// クロス初期化の主にワーカーへの登録
///
protected override void WorkerInit()
{
// BoneClothには不要
}
///
/// デフォーマーごとの使用頂点設定
/// 使用頂点に対して 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 && ActiveCount > 1)
if (MagicaPhysicsManager.Instance.IsDelay)
{
ClothTarget.ResetFuturePredictionParentTransform();
// 読み込みボーンの未来予測をリセットする
MagicaPhysicsManager.Instance.Particle.ResetFuturePredictionTransform(particleChunk);
}
}
// 書き込みボーンのフラグ設定
if (MagicaPhysicsManager.IsInstance())
{
//Debug.Log($"ChangeWriteBoneFlag:[{this.name}] Write:{IsCalculate} F:{Time.frameCount}");
MagicaPhysicsManager.Instance.Team.ChangeBoneFlag(TeamId, CullingMode, IsCalculate);
}
}
//=========================================================================================
///
/// 使用するトランスフォームをリストにして返す
///
///
public List GetTransformList()
{
List transformList = new List();
int cnt = clothTarget.RootCount;
for (int i = 0; i < cnt; i++)
{
var root = clothTarget.GetRoot(i);
if (root != null)
{
transformList.Add(root);
}
}
return transformList;
}
Transform GetUseTransform(int index)
{
int vindex = ClothData.useVertexList[index];
return useTransformList[vindex];
}
int UseTransformCount
{
get
{
return useTransformList.Count;
}
}
//=========================================================================================
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;
var mdata = MeshData;
if (mdata == null)
return Define.Error.MeshDataNull;
var mdataError = mdata.VerifyData();
if (mdataError != Define.Error.None)
return mdataError;
if (meshDataHash != mdata.SaveDataHash)
return Define.Error.MeshDataHashMismatch;
if (meshDataVersion != mdata.SaveDataVersion)
return Define.Error.MeshDataVersionMismatch;
if (useTransformList.Count == 0)
return Define.Error.UseTransformCountZero;
if (UseTransformCount != mdata.VertexCount)
return Define.Error.UseTransformCountMismatch;
if (clothTarget.RootCount != mdata.VertexCount)
return Define.Error.ClothTargetRootCountMismatch;
if (useTransformPositionList.Count != useTransformList.Count)
return Define.Error.UseTransformCountMismatch;
if (useTransformRotationList.Count != useTransformList.Count)
return Define.Error.UseTransformCountMismatch;
if (useTransformScaleList.Count != useTransformList.Count)
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("Clamp Position: ", clothParams.UseClampPositionLength ? cdata.VertexUseCount : 0);
StaticStringBuilder.AppendLine("Spring: ", clothParams.UseSpring ? cdata.VertexUseCount : 0);
StaticStringBuilder.Append("Adjust Rotation: ", cdata.VertexUseCount);
}
else if (err == Define.Error.EmptyData)
{
StaticStringBuilder.Append(Define.GetErrorMessage(err));
}
else
{
// エラー
StaticStringBuilder.AppendLine("This bone spring is in a state error!");
if (Application.isPlaying)
{
StaticStringBuilder.AppendLine("Execution stopped.");
}
else
{
StaticStringBuilder.AppendLine("Please recreate the bone spring 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(1.0f, 1.0f, false);
clothParams.SetGravity(false, -5.0f, -5.0f);
clothParams.SetDrag(true, 0.03f, 0.03f);
clothParams.SetMaxVelocity(true, 3.0f, 3.0f);
clothParams.SetWorldInfluence(2.0f, 0.5f, 1.0f);
clothParams.SetTeleport(false);
clothParams.SetClampDistanceRatio(false);
clothParams.SetClampPositionLength(true, 0.2f, 0.2f, 1.0f, 1.0f, 1.0f, 1.0f);
clothParams.SetClampRotationAngle(false);
clothParams.SetRestoreDistance(1.0f);
clothParams.SetRestoreRotation(false);
clothParams.SetSpring(true, 0.02f, 0.1f, 1.0f, 1.0f, 1.0f, 1.0f);
clothParams.SetAdjustRotation(ClothParams.AdjustMode.Fixed, 3.0f);
clothParams.SetTriangleBend(false);
clothParams.SetVolume(false);
clothParams.SetCollision(false, 0.1f);
clothParams.SetExternalForce(0.2f, 0.0f, 0.0f, 1.0f);
}
}
}