// Magica Cloth.
// Copyright (c) MagicaSoft, 2020-2022.
// https://magicasoft.jp
using System;
using System.Linq;
using System.Collections.Generic;
#if MAGICACLOTH_ECS
using Unity.Entities;
#endif
using UnityEngine;
using UnityEngine.LowLevel;
namespace MagicaCloth
{
///
/// MagicaCloth物理マネージャ
///
[HelpURL("https://magicasoft.jp/magica-cloth-physics-manager/")]
public partial class MagicaPhysicsManager : CreateSingleton
{
///
/// 更新管理
///
[SerializeField]
UpdateTimeManager updateTime = new UpdateTimeManager();
///
/// パーティクルデータ
///
PhysicsManagerParticleData particle = new PhysicsManagerParticleData();
///
/// トランスフォームデータ
///
PhysicsManagerBoneData bone = new PhysicsManagerBoneData();
///
/// メッシュデータ
///
PhysicsManagerMeshData mesh = new PhysicsManagerMeshData();
///
/// チームデータ
///
PhysicsManagerTeamData team = new PhysicsManagerTeamData();
///
/// 風データ
///
PhysicsManagerWindData wind = new PhysicsManagerWindData();
///
/// 全コンポーネントデータ
///
PhysicsManagerComponent component = new PhysicsManagerComponent();
///
/// 物理計算処理
///
PhysicsManagerCompute compute = new PhysicsManagerCompute();
///
/// Unity2021.2以降でのGetVertexBuffer()による高速書き込みの利用
/// 実行中は変更できない
///
[SerializeField]
private bool useFasterWrite = true;
// コンピュートシェーダー
private ComputeShader meshWriter = null;
//=========================================================================================
///
/// シミュレーション計算前イベント
/// Simulation pre-calculation event.
///
public PhysicsManagerPreUpdateEvent OnPreUpdate = new PhysicsManagerPreUpdateEvent();
///
/// シミュレーション計算後イベント
/// Simulation post-calculation event.
///
public PhysicsManagerPostUpdateEvent OnPostUpdate = new PhysicsManagerPostUpdateEvent();
//=========================================================================================
///
/// 遅延実行の有無
/// ランタイムで変更できるようにバッファリング
///
private bool useDelay = false;
///
/// Update()でのPlayerLoopチェック完了フラグ
///
private bool updatePlayerLoop = false;
///
/// マネージャ全体のアクティブフラグ
///
private bool isActive = true;
//=========================================================================================
public UpdateTimeManager UpdateTime
{
get
{
return updateTime;
}
}
public PhysicsManagerParticleData Particle
{
get
{
particle.SetParent(this);
return particle;
}
}
public PhysicsManagerBoneData Bone
{
get
{
bone.SetParent(this);
return bone;
}
}
public PhysicsManagerMeshData Mesh
{
get
{
mesh.SetParent(this);
return mesh;
}
}
public PhysicsManagerTeamData Team
{
get
{
team.SetParent(this);
return team;
}
}
public PhysicsManagerWindData Wind
{
get
{
wind.SetParent(this);
return wind;
}
}
public PhysicsManagerComponent Component
{
get
{
component.SetParent(this);
return component;
}
}
public PhysicsManagerCompute Compute
{
get
{
compute.SetParent(this);
return compute;
}
}
public bool IsDelay
{
get
{
return useDelay;
}
}
public bool IsActive
{
get
{
return isActive;
}
set
{
// アクティブはコンポーネントのenableフラグで行う
this.enabled = value;
}
}
public bool IsFasterWrite
{
get
{
if (useFasterWrite)
{
#if UNITY_2021_2_OR_NEWER
if (MeshWriterShader != null)
{
if (MeshWriterShader.IsSupported(0) && MeshWriterShader.IsSupported(1))
{
return true;
}
}
#endif
}
return false;
}
}
internal ComputeShader MeshWriterShader
{
get
{
if (meshWriter == null)
{
meshWriter = (ComputeShader)Resources.Load("MeshWriter");
}
return meshWriter;
}
}
//=========================================================================================
protected override void Awake()
{
base.Awake();
}
///
/// 初期化
///
protected override void InitSingleton()
{
Component.Create();
Particle.Create();
Bone.Create();
Mesh.Create();
Team.Create();
Wind.Create();
Compute.Create();
}
///
/// 2つ目の破棄されるマネージャの通知
///
///
protected override void DuplicateDetection(MagicaPhysicsManager duplicate)
{
// 設定をコピーする
UpdateMode = duplicate.UpdateMode;
UpdatePerSeccond = duplicate.UpdatePerSeccond;
FuturePredictionRate = duplicate.FuturePredictionRate;
}
protected void OnEnable()
{
if (isActive == false)
{
isActive = true;
Component.UpdateComponentStatus();
}
}
protected void OnDisable()
{
if (isActive == true)
{
isActive = false;
Component.UpdateComponentStatus();
}
}
private void Update()
{
// Unity2019.3以降の場合はUpdate時に一度カスタムループの登録チェックを行う
// すでに登録されていればスルーし、登録されていなければ再登録する
// これは他のアセットによりPlayerLoopが書き換えられてしまった場合の対策です
if (updatePlayerLoop == false)
{
//Debug.Log("Update check!!");
InitCustomGameLoop();
updatePlayerLoop = true;
}
}
private void FixedUpdate()
{
if (isActive)
{
UpdateTime.AddFixedUpdateCount();
}
}
///
/// 破棄
///
protected override void OnDestroy()
{
Compute.Dispose();
Wind.Dispose();
Team.Dispose();
Mesh.Dispose();
Bone.Dispose();
Particle.Dispose();
Component.Dispose();
base.OnDestroy();
}
//=========================================================================================
///
/// EarlyUpdateの後
///
private void AfterEarlyUpdate()
{
//Debug.Log($"After Early Update! F:{Time.frameCount}");
// フレーム開始時に行うチーム更新
Team.EarlyUpdateTeamAlways();
}
//private void BeforeFixedUpdate()
//{
// //Debug.Log("Before Fixed Update!" + Time.frameCount);
// // シミュレーションに必要なボーンの状態をもとに戻す(更新モード = UnityPhysics)
// if (Team.ActiveTeamCount > 0 && Team.PhysicsUpdateCount > 0)
// {
// Compute.InitJob();
// Compute.UpdateRestoreBone(PhysicsTeam.TeamUpdateMode.UnityPhysics);
// Compute.CompleteJob();
// }
//}
private void AfterFixedUpdate()
{
//Debug.Log("After Fixed Update!" + Time.frameCount);
// シミュレーションに必要なボーンの状態をもとに戻す(更新モード = UnityPhysics)
if (Team.ActiveTeamCount > 0 && Team.PhysicsUpdateCount > 0)
{
Compute.InitJob();
Compute.UpdateRestoreBone(PhysicsTeam.TeamUpdateMode.UnityPhysics);
Compute.CompleteJob();
}
}
///
/// Update()後の更新
///
private void AfterUpdate()
{
//Debug.Log("After Update!" + Time.frameCount);
// シミュレーションに必要なボーンの状態をもとに戻す(更新モード = Normal)
if (Team.ActiveTeamCount > 0 && Team.NormalUpdateCount > 0)
{
Compute.InitJob();
Compute.UpdateRestoreBone(PhysicsTeam.TeamUpdateMode.Normal);
Compute.CompleteJob();
}
}
///
/// LateUpdate()前の更新
///
//private void BeforeLateUpdate()
//{
// //Debug.Log("Before Late Update!" + Time.frameCount);
//}
///
/// LateUpdate()後の更新
///
private void AfterLateUpdate()
{
//Debug.Log("After Late Update!" + Time.frameCount);
//Debug.Log("dtime:" + Time.deltaTime + " smooth:" + Time.smoothDeltaTime);
// 遅延実行の切り替え判定
if (useDelay != UpdateTime.IsDelay)
{
if (useDelay == false)
{
// 結果の保持
Compute.UpdateSwapBuffer();
Compute.UpdateSyncBuffer();
}
useDelay = UpdateTime.IsDelay;
}
// パーティクルコンポーネントのデータ更新処理
Component.DataUpdateParticleComponent();
if (useDelay == false)
{
// 即時
OnPreUpdate.Invoke();
Compute.UpdateTeamAlways();
Compute.InitJob();
Compute.UpdateReadBone();
Compute.UpdateStartSimulation(updateTime);
Compute.UpdateWriteBone();
Compute.MeshCalculation();
Compute.UpdateCompleteSimulation();
Compute.NormalWritingMesh();
OnPostUpdate.Invoke();
}
}
///
/// PostLateUpdate.ScriptRunDelayedDynamicFrameRateの後
/// LateUpdate()やアセットバンドルロード完了コールバックでクロスコンポーネントをインスタンス化すると、
/// Start()が少し遅れてPostLateUpdateのScriptRunDelayedDynamicFrameRateで呼ばれることになる。
/// 遅延実行時にこの処理が入ると、すでにクロスシミュレーションのジョブが開始されているため、
/// Start()の初期化処理などでNativeリストにアクセスすると例外が発生してしまう。
/// 従って遅延実行時はクロスコンポーネントのStart()が完了するScriptRunDelayedDynamicFrameRate
/// の後にシミュレーションを開始するようにする。(v1.5.1)
///
private void PostLateUpdate()
{
//Debug.Log("Post Late Update!" + Time.frameCount);
if (useDelay)
{
// 遅延実行
OnPreUpdate.Invoke();
Compute.UpdateTeamAlways();
Compute.InitJob();
Compute.UpdateReadWriteBone();
Compute.UpdateStartSimulation(updateTime);
Compute.ScheduleJob();
Compute.MeshCalculation();
Compute.NormalWritingMesh(); // 前回の結果をメッシュに反映
//Debug.Log($"Delay Job! F:{Time.frameCount}");
}
}
///
/// レンダリング完了後の更新
///
private void AfterRendering()
{
//Debug.Log($"After Rendering Update! F:{Time.frameCount}");
if (useDelay)
{
// 遅延実行
// シミュレーション終了待機
Compute.UpdateCompleteSimulation();
// 結果の保持
Compute.UpdateSwapBuffer();
Compute.UpdateSyncBuffer();
OnPostUpdate.Invoke();
}
// シミュレーションに必要なボーンの状態をもとに戻す
//Compute.InitJob();
//Compute.UpdateRestoreBone();
//Compute.CompleteJob();
// FixedUpdateCountクリア
UpdateTime.ResetFixedUpdateCount();
}
//=========================================================================================
///
/// Reload Domain 対策
///
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void Init()
{
InitMember();
}
///
/// カスタム更新ループ登録
///
[RuntimeInitializeOnLoadMethod()]
public static void InitCustomGameLoop()
{
//Debug.Log("PhysicsManager.InitCustomGameLoop()");
PlayerLoopSystem playerLoop = PlayerLoop.GetCurrentPlayerLoop();
// すでに設定されているならばスルー
if (CheckRegist(ref playerLoop))
{
//Debug.Log("Skip!!");
return;
}
// MagicaCloth用PlayerLoopを追加
SetCustomGameLoop(ref playerLoop);
PlayerLoop.SetPlayerLoop(playerLoop);
}
///
/// playerLoopにMagicaClothで必要なCustomPlayerLoopを追加します
///
///
public static void SetCustomGameLoop(ref PlayerLoopSystem playerLoop)
{
#if false
// debug
foreach (var header in playerLoop.subSystemList)
{
Debug.LogFormat("------{0}------", header.type.Name);
foreach (var subSystem in header.subSystemList)
{
Debug.LogFormat("{0}.{1}", header.type.Name, subSystem.type.Name);
}
}
#endif
PlayerLoopSystem afterEarlyUpdate = new PlayerLoopSystem()
{
type = typeof(MagicaPhysicsManager),
updateDelegate = () =>
{
if (IsInstance())
{
Instance.AfterEarlyUpdate();
}
}
};
//PlayerLoopSystem beforeFixedUpdate = new PlayerLoopSystem()
//{
// type = typeof(MagicaPhysicsManager),
// updateDelegate = () =>
// {
// if (IsInstance())
// {
// Instance.BeforeFixedUpdate();
// }
// }
//};
PlayerLoopSystem afterFixedUpdate = new PlayerLoopSystem()
{
type = typeof(MagicaPhysicsManager),
updateDelegate = () =>
{
if (IsInstance())
{
Instance.AfterFixedUpdate();
}
}
};
PlayerLoopSystem afterUpdate = new PlayerLoopSystem()
{
type = typeof(MagicaPhysicsManager),
updateDelegate = () =>
{
if (IsInstance())
{
Instance.AfterUpdate();
}
}
};
//PlayerLoopSystem beforeLateUpdate = new PlayerLoopSystem()
//{
// type = typeof(MagicaPhysicsManager),
// updateDelegate = () =>
// {
// if (IsInstance())
// {
// Instance.BeforeLateUpdate();
// }
// }
//};
PlayerLoopSystem afterLateUpdate = new PlayerLoopSystem()
{
type = typeof(MagicaPhysicsManager),
updateDelegate = () =>
{
if (IsInstance())
{
Instance.AfterLateUpdate();
}
}
};
PlayerLoopSystem postLateUpdate = new PlayerLoopSystem()
{
type = typeof(MagicaPhysicsManager),
updateDelegate = () =>
{
if (IsInstance())
{
Instance.PostLateUpdate();
}
}
};
PlayerLoopSystem afterRendering = new PlayerLoopSystem()
{
type = typeof(MagicaPhysicsManager),
updateDelegate = () =>
{
if (IsInstance())
{
Instance.AfterRendering();
}
}
};
int sysIndex = 0;
int index = 0;
// early update
sysIndex = Array.FindIndex(playerLoop.subSystemList, (s) => s.type.Name == "EarlyUpdate");
PlayerLoopSystem earlyUpdateSystem = playerLoop.subSystemList[sysIndex];
var earlyUpdateSubsystemList = new List(earlyUpdateSystem.subSystemList);
earlyUpdateSubsystemList.Add(afterEarlyUpdate);
earlyUpdateSystem.subSystemList = earlyUpdateSubsystemList.ToArray();
playerLoop.subSystemList[sysIndex] = earlyUpdateSystem;
// fixed udpate
//sysIndex = Array.FindIndex(playerLoop.subSystemList, (s) => s.type.Name == "FixedUpdate");
//PlayerLoopSystem fixedUpdateSystem = playerLoop.subSystemList[sysIndex];
//var fixedUpdateSubsystemList = new List(fixedUpdateSystem.subSystemList);
//fixedUpdateSubsystemList.Insert(0, beforeFixedUpdate);
//fixedUpdateSystem.subSystemList = fixedUpdateSubsystemList.ToArray();
//playerLoop.subSystemList[sysIndex] = fixedUpdateSystem;
// after fixed update
sysIndex = Array.FindIndex(playerLoop.subSystemList, (s) => s.type.Name == "FixedUpdate");
PlayerLoopSystem fixedUpdateSystem = playerLoop.subSystemList[sysIndex];
var fixedUpdateSubsystemList = new List(fixedUpdateSystem.subSystemList);
index = fixedUpdateSubsystemList.FindIndex(h => h.type.Name.Contains("ScriptRunBehaviourFixedUpdate"));
fixedUpdateSubsystemList.Insert(index + 1, afterFixedUpdate); // FixedUpdate() after
fixedUpdateSystem.subSystemList = fixedUpdateSubsystemList.ToArray();
playerLoop.subSystemList[sysIndex] = fixedUpdateSystem;
// update
sysIndex = Array.FindIndex(playerLoop.subSystemList, (s) => s.type.Name == "Update");
PlayerLoopSystem updateSystem = playerLoop.subSystemList[sysIndex];
var updateSubsystemList = new List(updateSystem.subSystemList);
index = updateSubsystemList.FindIndex(h => h.type.Name.Contains("ScriptRunDelayedDynamicFrameRate"));
updateSubsystemList.Insert(index + 1, afterUpdate); // Update() after
updateSystem.subSystemList = updateSubsystemList.ToArray();
playerLoop.subSystemList[sysIndex] = updateSystem;
// late update
sysIndex = Array.FindIndex(playerLoop.subSystemList, (s) => s.type.Name == "PreLateUpdate");
PlayerLoopSystem lateUpdateSystem = playerLoop.subSystemList[sysIndex];
var lateUpdateSubsystemList = new List(lateUpdateSystem.subSystemList);
index = lateUpdateSubsystemList.FindIndex(h => h.type.Name.Contains("ScriptRunBehaviourLateUpdate"));
//lateUpdateSubsystemList.Insert(index, beforeLateUpdate); // LateUpdate() before
//lateUpdateSubsystemList.Insert(index + 2, afterLateUpdate); // LateUpdate() after
lateUpdateSubsystemList.Insert(index + 1, afterLateUpdate); // LateUpdate() after
lateUpdateSystem.subSystemList = lateUpdateSubsystemList.ToArray();
playerLoop.subSystemList[sysIndex] = lateUpdateSystem;
// post late update
sysIndex = Array.FindIndex(playerLoop.subSystemList, (s) => s.type.Name == "PostLateUpdate");
PlayerLoopSystem postLateUpdateSystem = playerLoop.subSystemList[sysIndex];
var postLateUpdateSubsystemList = new List(postLateUpdateSystem.subSystemList);
index = postLateUpdateSubsystemList.FindIndex(h => h.type.Name.Contains("ScriptRunDelayedDynamicFrameRate"));
postLateUpdateSubsystemList.Insert(index + 1, postLateUpdate); // postLateUpdate()
postLateUpdateSystem.subSystemList = postLateUpdateSubsystemList.ToArray();
playerLoop.subSystemList[sysIndex] = postLateUpdateSystem;
// rendering
sysIndex = Array.FindIndex(playerLoop.subSystemList, (s) => s.type.Name == "PostLateUpdate");
PlayerLoopSystem postLateSystem = playerLoop.subSystemList[sysIndex];
var postLateSubsystemList = new List(postLateSystem.subSystemList);
index = postLateSubsystemList.FindIndex(h => h.type.Name.Contains("FinishFrameRendering"));
postLateSubsystemList.Insert(index + 1, afterRendering); // rendering after
postLateSystem.subSystemList = postLateSubsystemList.ToArray();
playerLoop.subSystemList[sysIndex] = postLateSystem;
}
///
/// MagicaClothのカスタムループが登録されているかチェックする
///
///
///
private static bool CheckRegist(ref PlayerLoopSystem playerLoop)
{
var t = typeof(MagicaPhysicsManager);
foreach (var subloop in playerLoop.subSystemList)
{
if (subloop.subSystemList != null && subloop.subSystemList.Any(x => x.type == t))
{
return true;
}
}
return false;
}
}
}