CompositeRotationConstraint.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. // Magica Cloth.
  2. // Copyright (c) MagicaSoft, 2020-2022.
  3. // https://magicasoft.jp
  4. using System.Runtime.CompilerServices;
  5. using Unity.Burst;
  6. using Unity.Collections;
  7. using Unity.Jobs;
  8. using Unity.Mathematics;
  9. namespace MagicaCloth
  10. {
  11. /// <summary>
  12. /// 複合回転拘束(v1.11.0より)
  13. /// [Algorithm 2]
  14. /// ClampRotationとRestoreRotationを融合させたもの
  15. /// ルートラインベースに反復することで振動を抑える
  16. /// </summary>
  17. public class CompositeRotationConstraint : PhysicsManagerConstraint
  18. {
  19. /// <summary>
  20. /// 拘束データ
  21. /// Clmap/Restore共通
  22. /// </summary>
  23. [System.Serializable]
  24. public struct RotationData
  25. {
  26. /// <summary>
  27. /// 計算頂点インデックス
  28. /// </summary>
  29. public int vertexIndex;
  30. /// <summary>
  31. /// 親頂点インデックス
  32. /// </summary>
  33. public int parentVertexIndex;
  34. /// <summary>
  35. /// 親から自身への本来のローカル方向(単位ベクトル)
  36. /// </summary>
  37. public float3 localPos;
  38. /// <summary>
  39. /// 親から自身への本来のローカル回転
  40. /// </summary>
  41. public quaternion localRot;
  42. /// <summary>
  43. /// データが有効か判定する
  44. /// </summary>
  45. /// <returns></returns>
  46. public bool IsValid()
  47. {
  48. return vertexIndex > 0 || parentVertexIndex > 0;
  49. }
  50. }
  51. FixedChunkNativeArray<RotationData> dataList;
  52. [System.Serializable]
  53. public struct RootInfo
  54. {
  55. public ushort startIndex;
  56. public ushort dataLength;
  57. }
  58. FixedChunkNativeArray<RootInfo> rootInfoList;
  59. /// <summary>
  60. /// グループデータ
  61. /// </summary>
  62. public struct GroupData
  63. {
  64. public int teamId;
  65. public int useClamp;
  66. public int useRestore;
  67. /// <summary>
  68. /// 最大角度
  69. /// </summary>
  70. public CurveParam maxAngle;
  71. public CurveParam restorePower;
  72. /// <summary>
  73. /// 速度影響
  74. /// </summary>
  75. public float restoreVelocityInfluence;
  76. public ChunkData dataChunk;
  77. public ChunkData rootInfoChunk;
  78. }
  79. public FixedNativeList<GroupData> groupList;
  80. /// <summary>
  81. /// ルートごとのチームインデックス
  82. /// </summary>
  83. FixedChunkNativeArray<int> rootTeamList;
  84. /// <summary>
  85. /// 拘束データごとの作業バッファ
  86. /// </summary>
  87. FixedChunkNativeArray<float> lengthBuffer;
  88. //=========================================================================================
  89. public override void Create()
  90. {
  91. dataList = new FixedChunkNativeArray<RotationData>();
  92. rootInfoList = new FixedChunkNativeArray<RootInfo>();
  93. groupList = new FixedNativeList<GroupData>();
  94. rootTeamList = new FixedChunkNativeArray<int>();
  95. lengthBuffer = new FixedChunkNativeArray<float>();
  96. }
  97. public override void Release()
  98. {
  99. dataList.Dispose();
  100. rootInfoList.Dispose();
  101. groupList.Dispose();
  102. rootTeamList.Dispose();
  103. lengthBuffer.Dispose();
  104. }
  105. //=========================================================================================
  106. public int AddGroup(
  107. int teamId,
  108. bool useClamp,
  109. BezierParam maxAngle,
  110. bool useRestore,
  111. BezierParam restorePower,
  112. float velocityInfluence,
  113. RotationData[] dataArray,
  114. RootInfo[] rootInfoArray
  115. )
  116. {
  117. if (dataArray == null || dataArray.Length == 0 || rootInfoArray == null || rootInfoArray.Length == 0)
  118. return -1;
  119. var gdata = new GroupData();
  120. gdata.teamId = teamId;
  121. gdata.useClamp = useClamp ? 1 : 0;
  122. gdata.maxAngle.Setup(maxAngle);
  123. gdata.useRestore = useRestore ? 1 : 0;
  124. gdata.restorePower.Setup(restorePower);
  125. gdata.restoreVelocityInfluence = velocityInfluence;
  126. gdata.dataChunk = dataList.AddChunk(dataArray.Length);
  127. gdata.rootInfoChunk = rootInfoList.AddChunk(rootInfoArray.Length);
  128. // チャンクデータコピー
  129. dataList.ToJobArray().CopyFromFast(gdata.dataChunk.startIndex, dataArray);
  130. rootInfoList.ToJobArray().CopyFromFast(gdata.rootInfoChunk.startIndex, rootInfoArray);
  131. int group = groupList.Add(gdata);
  132. // ルートごとのチームインデックス
  133. var c = rootTeamList.AddChunk(rootInfoArray.Length);
  134. rootTeamList.Fill(c, teamId);
  135. // 作業バッファ
  136. lengthBuffer.AddChunk(dataArray.Length);
  137. return group;
  138. }
  139. public override void RemoveTeam(int teamId)
  140. {
  141. var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
  142. int group = teamData.compositeRotationGroupIndex;
  143. if (group >= 0)
  144. {
  145. var cdata = groupList[group];
  146. // チャンクデータ削除
  147. dataList.RemoveChunk(cdata.dataChunk);
  148. rootInfoList.RemoveChunk(cdata.rootInfoChunk);
  149. rootTeamList.RemoveChunk(cdata.rootInfoChunk);
  150. lengthBuffer.RemoveChunk(cdata.dataChunk);
  151. // データ削除
  152. groupList.Remove(group);
  153. }
  154. }
  155. public void ChangeParam(
  156. int teamId,
  157. bool useClamp,
  158. BezierParam maxAngle,
  159. bool useRestore,
  160. BezierParam restorePower,
  161. float velocityInfluence
  162. )
  163. {
  164. var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
  165. int group = teamData.compositeRotationGroupIndex;
  166. if (group < 0)
  167. return;
  168. var gdata = groupList[group];
  169. gdata.useClamp = useClamp ? 1 : 0;
  170. gdata.maxAngle.Setup(maxAngle);
  171. gdata.useRestore = useRestore ? 1 : 0;
  172. gdata.restorePower.Setup(restorePower);
  173. gdata.restoreVelocityInfluence = velocityInfluence;
  174. groupList[group] = gdata;
  175. }
  176. //=========================================================================================
  177. /// <summary>
  178. /// 拘束の解決
  179. /// </summary>
  180. /// <param name="dtime"></param>
  181. /// <param name="jobHandle"></param>
  182. /// <returns></returns>
  183. public override JobHandle SolverConstraint(int runCount, float dtime, float updatePower, int iteration, JobHandle jobHandle)
  184. {
  185. if (groupList.Count > 0)
  186. {
  187. // 回転拘束(ルートラインごと)
  188. var job = new RotationRootLineJob()
  189. {
  190. updatePower = updatePower,
  191. runCount = runCount,
  192. maxMoveSpeed = dtime * Define.Compute.ClampRotationMaxVelocity2, // 最大2.0m/s
  193. dataList = dataList.ToJobArray(),
  194. rootInfoList = rootInfoList.ToJobArray(),
  195. rootTeamList = rootTeamList.ToJobArray(),
  196. groupList = groupList.ToJobArray(),
  197. teamDataList = Manager.Team.teamDataList.ToJobArray(),
  198. teamGravityList = Manager.Team.teamGravityList.ToJobArray(),
  199. depthList = Manager.Particle.depthList.ToJobArray(),
  200. flagList = Manager.Particle.flagList.ToJobArray(),
  201. frictionList = Manager.Particle.frictionList.ToJobArray(),
  202. basePosList = Manager.Particle.basePosList.ToJobArray(),
  203. baseRotList = Manager.Particle.baseRotList.ToJobArray(),
  204. nextPosList = Manager.Particle.InNextPosList.ToJobArray(),
  205. nextRotList = Manager.Particle.InNextRotList.ToJobArray(),
  206. posList = Manager.Particle.posList.ToJobArray(),
  207. lengthBuffer = lengthBuffer.ToJobArray(),
  208. };
  209. jobHandle = job.Schedule(rootTeamList.Length, 4, jobHandle);
  210. }
  211. return jobHandle;
  212. }
  213. /// <summary>
  214. /// 回転拘束ジョブ[Algorithm 2]
  215. /// </summary>
  216. [BurstCompile]
  217. struct RotationRootLineJob : IJobParallelFor
  218. {
  219. public float updatePower;
  220. public int runCount;
  221. public float maxMoveSpeed;
  222. [Unity.Collections.ReadOnly]
  223. public NativeArray<RotationData> dataList;
  224. [Unity.Collections.ReadOnly]
  225. public NativeArray<RootInfo> rootInfoList;
  226. [Unity.Collections.ReadOnly]
  227. public NativeArray<int> rootTeamList;
  228. [Unity.Collections.ReadOnly]
  229. public NativeArray<GroupData> groupList;
  230. [Unity.Collections.ReadOnly]
  231. public NativeArray<PhysicsManagerTeamData.TeamData> teamDataList;
  232. [Unity.Collections.ReadOnly]
  233. public NativeArray<CurveParam> teamGravityList;
  234. [Unity.Collections.ReadOnly]
  235. public NativeArray<float> depthList;
  236. [Unity.Collections.ReadOnly]
  237. public NativeArray<PhysicsManagerParticleData.ParticleFlag> flagList;
  238. [Unity.Collections.ReadOnly]
  239. public NativeArray<float> frictionList;
  240. [Unity.Collections.ReadOnly]
  241. public NativeArray<float3> basePosList;
  242. [Unity.Collections.ReadOnly]
  243. public NativeArray<quaternion> baseRotList;
  244. [NativeDisableParallelForRestriction]
  245. public NativeArray<float3> nextPosList;
  246. [NativeDisableParallelForRestriction]
  247. public NativeArray<quaternion> nextRotList;
  248. [NativeDisableParallelForRestriction]
  249. public NativeArray<float3> posList;
  250. [NativeDisableParallelForRestriction]
  251. public NativeArray<float> lengthBuffer;
  252. // ルートラインごと
  253. public void Execute(int rootIndex)
  254. {
  255. // チーム
  256. int teamIndex = rootTeamList[rootIndex];
  257. if (teamIndex == 0)
  258. return;
  259. var team = teamDataList[teamIndex];
  260. if (team.IsActive() == false || team.compositeRotationGroupIndex < 0)
  261. return;
  262. // 更新確認
  263. if (team.IsUpdate(runCount) == false)
  264. return;
  265. // グループデータ
  266. var gdata = groupList[team.compositeRotationGroupIndex];
  267. if (gdata.useClamp == 0 && gdata.useRestore == 0)
  268. return;
  269. // アニメーションされた姿勢の使用
  270. bool useAnimatedPose = team.IsFlag(PhysicsManagerTeamData.Flag_AnimatedPose);
  271. // データ
  272. var rootInfo = rootInfoList[rootIndex];
  273. int dataIndex = rootInfo.startIndex + gdata.dataChunk.startIndex;
  274. int dataCount = rootInfo.dataLength;
  275. int pstart = team.particleChunk.startIndex;
  276. // (1)現在の親からのベクトル長を保持する
  277. if (gdata.useClamp == 1)
  278. {
  279. for (int i = 0; i < dataCount; i++)
  280. {
  281. var data = dataList[dataIndex + i];
  282. int pindex = data.parentVertexIndex;
  283. if (pindex < 0)
  284. continue;
  285. var index = data.vertexIndex;
  286. index += pstart;
  287. pindex += pstart;
  288. var npos = nextPosList[index];
  289. var ppos = nextPosList[pindex];
  290. // 現在ベクトル長
  291. float vlen = math.distance(npos, ppos);
  292. lengthBuffer[dataIndex + i] = vlen;
  293. }
  294. }
  295. // 2回以上反復することで安定する
  296. const int iteration = 2;
  297. for (int j = 0; j < iteration; j++)
  298. {
  299. // (2)ルートラインを親から処理する
  300. for (int i = 0; i < dataCount; i++)
  301. {
  302. var data = dataList[dataIndex + i];
  303. int pindex = data.parentVertexIndex;
  304. if (pindex < 0)
  305. continue;
  306. int index = data.vertexIndex;
  307. index += pstart;
  308. pindex += pstart;
  309. // 子の情報
  310. var cflag = flagList[index];
  311. if (cflag.IsValid() == false)
  312. continue;
  313. var cpos = nextPosList[index];
  314. var crot = nextRotList[index];
  315. float cdepth = depthList[index];
  316. float cfriction = frictionList[index];
  317. float cmoveratio = math.saturate(1.0f - cfriction * Define.Compute.FrictionMoveRatio);
  318. // 親の情報
  319. var pflag = flagList[pindex];
  320. var ppos = nextPosList[pindex];
  321. var pbrot = baseRotList[pindex];
  322. var prot = nextRotList[pindex];
  323. float pfriction = frictionList[pindex];
  324. float pmoveratio = math.saturate(1.0f - pfriction * Define.Compute.FrictionMoveRatio);
  325. // 重力ベクトルを決定する
  326. var gravity = math.abs(teamGravityList[teamIndex].Evaluate(cdepth));
  327. float3 gravityVector = gravity > Define.Compute.Epsilon ? team.gravityDirection : 0;
  328. // 親からの姿勢
  329. float3 localPos = data.localPos;
  330. quaternion localRot = data.localRot;
  331. if (useAnimatedPose)
  332. {
  333. // 親からの姿勢を常に計算する
  334. var brot = baseRotList[index];
  335. var bpos = basePosList[index];
  336. var pbpos = basePosList[pindex];
  337. var v = bpos - pbpos;
  338. v = math.normalize(v);
  339. var ipq = math.inverse(pbrot);
  340. localPos = math.mul(ipq, v);
  341. localRot = math.mul(ipq, brot);
  342. }
  343. else
  344. {
  345. // マイナススケール対応
  346. localPos = localPos * team.scaleDirection;
  347. localRot = localRot.value * team.quaternionScale;
  348. }
  349. //=====================================================
  350. // Clamp
  351. //=====================================================
  352. if (gdata.useClamp == 1)
  353. {
  354. // 親基準回転
  355. var trot = pflag.IsMove() ? prot : pbrot; // 自身の親
  356. //var trot = pbrot; // 常にベース姿勢
  357. // 現在ベクトル
  358. float3 v = cpos - ppos;
  359. // 本来のベクトル
  360. float3 tv = math.mul(trot, localPos);
  361. // ベクトル長修正
  362. float vlen = math.length(v); // 最新の距離(※これは伸びる場合があるが、一番安定している)
  363. float blen = lengthBuffer[dataIndex + i]; // 計算前の距離
  364. vlen = math.lerp(vlen, blen, 0.5f); // 計算前の距離に補間する(0.2?)
  365. v = math.normalize(v) * vlen;
  366. // ベクトル角度クランプ
  367. float maxAngleDeg = gdata.maxAngle.Evaluate(cdepth);
  368. float maxAngleRad = math.radians(maxAngleDeg);
  369. float angle = math.acos(math.dot(v, tv));
  370. float qratio = 0.0f;
  371. #if true
  372. if (cflag.IsFixed() == false)
  373. {
  374. float3 rv = v;
  375. if (angle > maxAngleRad)
  376. {
  377. MathUtility.ClampAngle(v, tv, maxAngleRad, out rv);
  378. qratio = 1.0f - maxAngleRad / angle;
  379. }
  380. // 回転中心割合
  381. const float rotRatio = 0.5f; // 0.5以外は安定しない
  382. float3 rotPos = ppos + v * rotRatio;
  383. // 親と子のそれぞれの更新位置
  384. float3 pfpos = rotPos - rv * rotRatio;
  385. float3 cfpos = rotPos + rv * (1.0f - rotRatio);
  386. // 加算
  387. float3 padd = pfpos - ppos;
  388. float3 cadd = cfpos - cpos;
  389. // 最大速度(一旦停止)
  390. //padd = MathUtility.ClampVector(padd, 0.0f, maxMoveSpeed);
  391. //cadd = MathUtility.ClampVector(cadd, 0.0f, maxMoveSpeed);
  392. // 摩擦考慮
  393. padd *= pmoveratio;
  394. cadd *= cmoveratio;
  395. // 移動影響
  396. // 最大角度が狭いほど影響力を強くする
  397. float influence = math.lerp(0.5f, 0.2f, math.pow(math.saturate(maxAngleDeg / 90.0f), 0.5f));
  398. // 書き出し
  399. if (cflag.IsMove())
  400. {
  401. nextPosList[index] = cpos + cadd;
  402. // 速度影響
  403. posList[index] = posList[index] + (cadd * (1.0f - influence));
  404. cpos += cadd;
  405. }
  406. if (pflag.IsMove())
  407. {
  408. nextPosList[pindex] = ppos + padd;
  409. // 速度影響
  410. posList[pindex] = posList[pindex] + (padd * (1.0f - influence));
  411. ppos += padd;
  412. }
  413. // ベクトル補正
  414. v = cpos - ppos;
  415. }
  416. #else
  417. if (cflag.IsFixed() == false)
  418. {
  419. float3 rv = v;
  420. if (angle > maxAngle)
  421. {
  422. MathUtility.ClampAngle(v, tv, maxAngle, out rv);
  423. qratio = 1.0f - maxAngle / angle;
  424. }
  425. float3 cfpos = ppos + math.normalize(rv) * vlen;
  426. // 加算
  427. //float3 padd = pfpos - ppos;
  428. float3 cadd = cfpos - cpos;
  429. // 摩擦考慮
  430. //padd *= pmoveratio;
  431. cadd *= cmoveratio;
  432. // 書き出し
  433. const float influence = 0.2f;
  434. if (cflag.IsMove())
  435. {
  436. nextPosList[index] = cpos + cadd;
  437. // 速度影響
  438. posList[index] = posList[index] + (cadd * (1.0f - influence));
  439. cpos += cadd;
  440. }
  441. // ベクトル補正
  442. v = cpos - ppos;
  443. }
  444. #endif
  445. //=====================================================
  446. // 回転補正
  447. //=====================================================
  448. var nrot = math.mul(trot, localRot);
  449. var q = MathUtility.FromToRotation(tv, v);
  450. nrot = math.mul(q, nrot);
  451. nextRotList[index] = nrot;
  452. }
  453. //=====================================================
  454. // Restore
  455. //=====================================================
  456. if (gdata.useRestore == 1)
  457. {
  458. // 現在ベクトル
  459. float3 v = cpos - ppos;
  460. // 本来のベクトル(常にベース回転から計算)
  461. float3 tv = math.mul(pbrot, localPos);
  462. float restorePower = gdata.restorePower.Evaluate(cdepth);
  463. restorePower = 1.0f - math.pow(1.0f - restorePower, updatePower);
  464. // 球面線形補間
  465. var q = MathUtility.FromToRotation(v, tv, restorePower);
  466. float3 rv = math.mul(q, v);
  467. // 回転中心割合
  468. float rotRatio = GetRotRatio(tv, gravityVector, gravity);
  469. float3 rotPos = ppos + v * rotRatio;
  470. // 親と子のそれぞれの更新位置
  471. float3 pfpos = rotPos - rv * rotRatio;
  472. float3 cfpos = rotPos + rv * (1.0f - rotRatio);
  473. // 加算
  474. float3 padd = pfpos - ppos;
  475. float3 cadd = cfpos - cpos;
  476. // 摩擦考慮
  477. padd *= pmoveratio;
  478. cadd *= cmoveratio;
  479. // 書き出し
  480. float influence = gdata.restoreVelocityInfluence;
  481. if (cflag.IsMove())
  482. {
  483. nextPosList[index] = cpos + cadd;
  484. // 速度影響
  485. posList[index] = posList[index] + (cadd * (1.0f - influence));
  486. cpos += cadd;
  487. }
  488. if (pflag.IsMove())
  489. {
  490. nextPosList[pindex] = ppos + padd;
  491. // 速度影響
  492. posList[pindex] = posList[pindex] + (padd * (1.0f - influence));
  493. ppos += padd;
  494. }
  495. }
  496. }
  497. }
  498. }
  499. /// <summary>
  500. /// 補間目標ベクトルと重力ベクトルにより回転の中央割合を決定する
  501. /// 0.4では大きく安定するが動きが鈍くなる、0.2では動きは良いが補間ベクトルが重力天井方向だと安定しない
  502. /// そのため重力ベクトルとの角度により一番安定する割合を決定する
  503. /// </summary>
  504. /// <param name="tv"></param>
  505. /// <returns></returns>
  506. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  507. float GetRotRatio(float3 tv, float3 gravityVector, float gravity, float minRatio = 0.25f, float maxRatio = 0.45f)
  508. {
  509. #if true
  510. // 重力方向割合(0.0-1.0)
  511. float dot = math.dot(math.normalize(tv), gravityVector);
  512. dot = dot * 0.5f + 0.5f;
  513. // 角度による増加曲線は重力の強さにより調整
  514. float pow = math.lerp(4.0f, 1.0f, math.saturate(gravity / 9.8f)); // 4.0 - 1.0?
  515. // 角度による増加曲線(0.0-1.0)
  516. dot = math.pow(dot, pow);
  517. // 最終的な回転割合は角度率により線形補間する
  518. // 重力0の場合は中間値が使用される
  519. //float rotRatio = math.lerp(0.25f, 0.45f, dot);
  520. float rotRatio = math.lerp(minRatio, maxRatio, dot);
  521. return rotRatio;
  522. #else
  523. return 0.3f; // 最初の調整
  524. #endif
  525. }
  526. }
  527. }
  528. }