// 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; } } }