// 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-mesh-spring/")]
[AddComponentMenu("MagicaCloth/MagicaMeshSpring", 100)]
public class MagicaMeshSpring : BaseCloth
{
///
/// データバージョン
///
private const int DATA_VERSION = 7;
///
/// エラーデータバージョン
///
private const int ERR_DATA_VERSION = 3;
// 対象仮想メッシュデフォーマー
[SerializeField]
private MagicaVirtualDeformer virtualDeformer = null;
[SerializeField]
private int virtualDeformerHash;
[SerializeField]
private int virtualDeformerVersion;
// センタートランスフォーム
[SerializeField]
private Transform centerTransform = null;
public enum Axis
{
X,
Y,
Z,
InverseX,
InverseY,
InverseZ,
}
[SerializeField]
private Axis directionAxis;
[SerializeField]
private SpringData springData = null;
[SerializeField]
private int springDataHash;
[SerializeField]
private int springDataVersion;
//=========================================================================================
public override ComponentType GetComponentType()
{
return ComponentType.MeshSpring;
}
//=========================================================================================
public override int GetDataHash()
{
int hash = base.GetDataHash();
hash += virtualDeformer.GetDataHash();
hash += centerTransform.GetDataHash();
hash += SpringData.GetDataHash();
return hash;
}
//=========================================================================================
public VirtualMeshDeformer Deformer
{
get
{
if (virtualDeformer != null)
return virtualDeformer.Deformer;
return null;
}
}
public SpringData SpringData
{
get
{
#if UNITY_EDITOR
if (Application.isPlaying)
return springData;
else
{
// unity2019.3で参照がnullとなる不具合の対処(臨時)
var so = new SerializedObject(this);
return so.FindProperty("springData").objectReferenceValue as SpringData;
}
#else
return springData;
#endif
}
}
public int UseVertexCount
{
get
{
if (SpringData == null)
return 0;
else
return SpringData.UseVertexCount;
}
}
public Transform CenterTransform
{
get
{
return centerTransform;
}
set
{
centerTransform = value;
}
}
public Axis DirectionAxis
{
get
{
return directionAxis;
}
set
{
directionAxis = value;
}
}
public Vector3 CenterTransformDirection
{
get
{
Vector3 dir = Vector3.forward;
if (centerTransform)
{
switch (directionAxis)
{
case Axis.X:
dir = centerTransform.right;
break;
case Axis.Y:
dir = centerTransform.up;
break;
case Axis.Z:
dir = centerTransform.forward;
break;
case Axis.InverseX:
dir = -centerTransform.right;
break;
case Axis.InverseY:
dir = -centerTransform.up;
break;
case Axis.InverseZ:
dir = -centerTransform.forward;
break;
}
}
return dir;
}
}
public SpringData.DeformerData GetDeformerData()
{
return SpringData.deformerData;
}
//=========================================================================================
protected override void Reset()
{
base.Reset();
ResetParams();
}
protected override void OnValidate()
{
base.OnValidate();
}
//=========================================================================================
///
/// クロス初期化
///
protected override void ClothInit()
{
// 中央トランスフォームに移動パーティクルを1つ設定する(これが揺れる)
// クロスデータはこの場で作成する
ClothData cdata = ShareDataObject.CreateShareData("ClothData_work");
cdata.selectionData.Add(SelectionData.Move);
cdata.vertexFlagLevelList.Add(0);
cdata.vertexDepthList.Add(0);
cdata.rootList.Add(0);
cdata.useVertexList.Add(0);
cdata.initScale = SpringData.initScale;
cdata.SaveDataHash = 1;
cdata.SaveDataVersion = cdata.GetVersion();
cdata.clampRotationAlgorithm = ClothParams.Algorithm.Algorithm_2;
cdata.restoreRotationAlgorithm = ClothParams.Algorithm.Algorithm_2;
cdata.triangleBendAlgorithm = ClothParams.Algorithm.Algorithm_2;
ClothData = cdata;
// エラーが出ないように
clothDataHash = cdata.SaveDataHash;
clothDataVersion = cdata.SaveDataVersion;
// クロス初期化
base.ClothInit();
// スプリングではClampPositonの速度制限は無視する
MagicaPhysicsManager.Instance.Team.SetFlag(TeamId, PhysicsManagerTeamData.Flag_IgnoreClampPositionVelocity, true);
}
protected override void ClothActive()
{
base.ClothActive();
}
///
/// 頂点ごとのパーティクルフラグ設定(不要な場合は0)
///
///
///
protected override uint UserFlag(int index)
{
uint flag = 0;
flag |= PhysicsManagerParticleData.Flag_Transform_Read_Base; // トランスフォームをbasePos/baseRotに読み込み
flag |= PhysicsManagerParticleData.Flag_Transform_Read_Rot; // トランスフォームをrotに読み込む
return flag;
}
///
/// 頂点ごとの連動トランスフォーム設定(不要な場合はnull)
///
///
///
protected override Transform UserTransform(int index)
{
return CenterTransform;
}
///
/// 頂点ごとの連動トランスフォームのLocalPositionを返す(不要な場合は0)
///
///
///
protected override float3 UserTransformLocalPosition(int vindex)
{
return CenterTransform.localPosition;
}
///
/// 頂点ごとの連動トランスフォームのLocalRotationを返す(不要な場合はquaternion.identity)
///
///
///
protected override quaternion UserTransformLocalRotation(int vindex)
{
return CenterTransform.localRotation;
}
///
/// デフォーマーが必須か返す
///
///
public override bool IsRequiresDeformer()
{
return true;
}
///
/// デフォーマーを返す
///
///
///
public override BaseMeshDeformer GetDeformer()
{
if (virtualDeformer)
{
return virtualDeformer.Deformer;
}
return null;
}
///
/// クロス初期化時に必要なMeshDataを返す(不要ならnull)
///
///
protected override MeshData GetMeshData()
{
// MeshSpringeには不要
return null;
}
///
/// クロス初期化の主にワーカーへの登録
///
protected override void WorkerInit()
{
// センターパーティクル
int pindex = ParticleChunk.startIndex;
// デフォーマーごとの設定
SpringMeshWorker worker = MagicaPhysicsManager.Instance.Compute.SpringMeshWorker;
{
// デフォーマー取得
var deformer = GetDeformer();
Debug.Assert(deformer != null);
deformer.Init();
// スプリングデータ取得
var data = GetDeformerData();
Debug.Assert(data != null);
// スプリングワーカー設定
var minfo = MagicaPhysicsManager.Instance.Mesh.GetVirtualMeshInfo(deformer.MeshIndex);
for (int j = 0; j < data.UseVertexCount; j++)
{
int vindex = data.useVertexIndexList[j];
worker.Add(TeamId, minfo.vertexChunk.startIndex + vindex, pindex, data.weightList[j]);
}
}
}
///
/// デフォーマーごとの使用頂点設定
/// 使用頂点に対して AddUseVertex() / RemoveUseVertex() を実行する
///
///
///
protected override void SetDeformerUseVertex(bool sw, BaseMeshDeformer deformer)
{
var data = GetDeformerData();
int vcnt = data.UseVertexCount;
for (int j = 0; j < vcnt; j++)
{
int vindex = data.useVertexIndexList[j];
if (sw)
deformer.AddUseVertex(vindex, false);
else
deformer.RemoveUseVertex(vindex, false);
}
}
///
/// UnityPhyiscsでの更新の変更
/// 継承クラスは自身の使用するボーンの状態更新などを記述する
///
///
protected override void ChangeUseUnityPhysics(bool sw)
{
base.ChangeUseUnityPhysics(sw);
// デフォーマに伝達
virtualDeformer?.SetUseUnityPhysics(sw);
}
protected override void OnChangeCalculation()
{
base.OnChangeCalculation();
if (IsCalculate)
{
if (MagicaPhysicsManager.Instance.IsDelay)
{
// 読み込みボーンの未来予測をリセットする
MagicaPhysicsManager.Instance.Particle.ResetFuturePredictionTransform(particleChunk);
}
}
}
//=========================================================================================
public override int GetVersion()
{
return DATA_VERSION;
}
///
/// エラーとするデータバージョンを取得する
///
///
public override int GetErrorVersion()
{
return ERR_DATA_VERSION;
}
///
/// データを検証して結果を格納する
///
///
public override void CreateVerifyData()
{
base.CreateVerifyData();
virtualDeformerHash = virtualDeformer.SaveDataHash;
virtualDeformerVersion = virtualDeformer.SaveDataVersion;
springDataHash = SpringData.SaveDataHash;
springDataVersion = SpringData.SaveDataVersion;
}
///
/// 現在のデータが正常(実行できる状態)か返す
///
///
public override Define.Error VerifyData()
{
var baseError = base.VerifyData();
if (baseError != Define.Error.None)
return baseError;
if (virtualDeformer == null)
return Define.Error.DeformerNull;
var vdeformerError = virtualDeformer.VerifyData();
if (vdeformerError != Define.Error.None)
return vdeformerError;
if (virtualDeformerHash != virtualDeformer.SaveDataHash)
return Define.Error.DeformerHashMismatch;
if (virtualDeformerVersion != virtualDeformer.SaveDataVersion)
return Define.Error.DeformerVersionMismatch;
if (centerTransform == null)
return Define.Error.CenterTransformNull;
var sdata = SpringData;
if (sdata == null)
return Define.Error.SpringDataNull;
var sdataError = sdata.VerifyData();
if (sdataError != Define.Error.None)
return sdataError;
if (springDataHash != sdata.SaveDataHash)
return Define.Error.SpringDataHashMismatch;
if (springDataVersion != sdata.SaveDataVersion)
return Define.Error.SpringDataVersionMismatch;
return Define.Error.None;
}
public override string GetInformation()
{
StaticStringBuilder.Clear();
var err = VerifyData();
if (err == Define.Error.None)
{
// OK
StaticStringBuilder.AppendLine("Active: ", Status.IsActive);
StaticStringBuilder.AppendLine($"Visible: {IsVisible}");
StaticStringBuilder.AppendLine($"Calculation:{IsCalculate}");
StaticStringBuilder.Append("Use Deformer Vertex: ", UseVertexCount);
}
else if (err == Define.Error.EmptyData)
{
StaticStringBuilder.Append(Define.GetErrorMessage(err));
}
else
{
// エラー
StaticStringBuilder.AppendLine("This mesh spring is in a state error!");
if (Application.isPlaying)
{
StaticStringBuilder.AppendLine("Execution stopped.");
}
else
{
StaticStringBuilder.AppendLine("Please recreate the mesh spring data.");
}
StaticStringBuilder.Append(Define.GetErrorMessage(err));
}
return StaticStringBuilder.ToString();
}
///
/// デフォーマーの検証
///
public void VerifyDeformer()
{
}
//=========================================================================================
///
/// ボーンを置換する
///
///
public override void ReplaceBone(Dictionary boneReplaceDict)
{
if (centerTransform)
{
centerTransform = MeshUtility.GetReplaceBone(centerTransform, boneReplaceDict);
}
}
///
/// 現在使用しているボーンを格納して返す
///
///
public override HashSet GetUsedBones()
{
var bones = base.GetUsedBones();
bones.Add(centerTransform);
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 t = CenterTransform;
if (t == null)
return 0;
wposList.Add(t.position);
wnorList.Add(t.forward);
var up = t.up;
wtanList.Add(up);
return 1;
}
///
/// メッシュのトライアングルリストを返す(エディタ用)
///
///
public override List GetEditorTriangleList()
{
return null;
}
///
/// メッシュのラインリストを返す(エディタ用)
///
///
public override List GetEditorLineList()
{
return null;
}
//=========================================================================================
///
/// 頂点の選択状態をリストにして返す(エディタ用)
/// 選択状態は ClothSelection.Invalid / ClothSelection.Fixed / ClothSelection.Move
/// すべてがInvalidならばnullを返す
///
///
public override List GetSelectionList()
{
return null;
}
///
/// 頂点の使用状態をリストにして返す(エディタ用)
/// 数値が1以上ならば使用中とみなす
/// すべて使用状態ならばnullを返す
///
///
public override List GetUseList()
{
return null;
}
//=========================================================================================
///
/// 共有データオブジェクト収集
///
///
public override List GetAllShareDataObject()
{
var sdata = base.GetAllShareDataObject();
sdata.Add(SpringData);
return sdata;
}
///
/// sourceの共有データを複製して再セットする
/// 再セットした共有データを返す
///
///
///
public override ShareDataObject DuplicateShareDataObject(ShareDataObject source)
{
var sdata = base.DuplicateShareDataObject(source);
if (sdata != null)
return sdata;
if (SpringData == source)
{
//springData = Instantiate(SpringData);
springData = ShareDataObject.Clone(SpringData);
return springData;
}
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.01f, 0.01f);
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.1f, 0.1f, 1.0f, 1.0f, 1.0f, 0.2f);
clothParams.SetClampRotationAngle(false);
clothParams.SetRestoreDistance(1.0f);
clothParams.SetRestoreRotation(false);
clothParams.SetSpring(true, 0.02f, 0.14f, 1.0f, 1.0f, 1.0f, 1.0f);
clothParams.SetSpringDirectionAtten(1.0f, 0.0f, 0.6f);
clothParams.SetSpringDistanceAtten(1.0f, 0.0f, 0.4f);
clothParams.SetAdjustRotation(ClothParams.AdjustMode.Fixed, 5.0f);
clothParams.SetTriangleBend(false);
clothParams.SetVolume(false);
clothParams.SetCollision(false, 0.1f);
clothParams.SetExternalForce(0.2f, 0.0f, 0.0f, 1.0f);
}
}
}