PenetrationConstraint.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. // Magica Cloth.
  2. // Copyright (c) MagicaSoft, 2020-2022.
  3. // https://magicasoft.jp
  4. using Unity.Burst;
  5. using Unity.Collections;
  6. using Unity.Jobs;
  7. using Unity.Mathematics;
  8. namespace MagicaCloth
  9. {
  10. /// <summary>
  11. /// 浸透制限拘束
  12. /// </summary>
  13. public class PenetrationConstraint : PhysicsManagerConstraint
  14. {
  15. /// <summary>
  16. /// 浸透制限データ
  17. /// todo:共有可能
  18. /// </summary>
  19. [System.Serializable]
  20. public struct PenetrationData
  21. {
  22. /// <summary>
  23. /// 計算頂点インデックス
  24. /// </summary>
  25. public short vertexIndex;
  26. /// <summary>
  27. /// コライダー配列インデックス
  28. /// </summary>
  29. public short colliderIndex;
  30. /// <summary>
  31. /// コライダーローカル座標(中心軸)
  32. /// </summary>
  33. public float3 localPos;
  34. /// <summary>
  35. /// 押し出しローカル方向(単位ベクトル)
  36. /// </summary>
  37. public float3 localDir;
  38. /// <summary>
  39. /// パーティクルへの距離(オリジナル位置)
  40. /// </summary>
  41. public float distance;
  42. public bool IsValid()
  43. {
  44. return vertexIndex >= 0;
  45. }
  46. }
  47. FixedChunkNativeArray<PenetrationData> dataList;
  48. /// <summary>
  49. /// ローカルパーティクルインデックスごとのデータ参照情報
  50. /// </summary>
  51. FixedChunkNativeArray<ReferenceDataIndex> refDataList;
  52. /// <summary>
  53. /// BonePenetration用データ
  54. /// 頂点に対するローカル浸透方向
  55. /// </summary>
  56. FixedChunkNativeArray<float3> bonePenetrationDataList;
  57. /// <summary>
  58. /// グループごとの拘束データ
  59. /// </summary>
  60. public struct GroupData
  61. {
  62. public int teamId;
  63. public int active;
  64. /// <summary>
  65. /// (0=Surface, 1=Collider, 2=Bone)
  66. /// </summary>
  67. public int mode;
  68. public float maxDepth;
  69. public CurveParam radius;
  70. public CurveParam distance;
  71. public ChunkData dataChunk;
  72. public ChunkData refDataChunk;
  73. public ChunkData bonePenetrationDataChunk;
  74. }
  75. public FixedNativeList<GroupData> groupList;
  76. //=========================================================================================
  77. public override void Create()
  78. {
  79. groupList = new FixedNativeList<GroupData>();
  80. dataList = new FixedChunkNativeArray<PenetrationData>();
  81. refDataList = new FixedChunkNativeArray<ReferenceDataIndex>();
  82. bonePenetrationDataList = new FixedChunkNativeArray<float3>();
  83. }
  84. public override void Release()
  85. {
  86. groupList.Dispose();
  87. dataList.Dispose();
  88. refDataList.Dispose();
  89. bonePenetrationDataList.Dispose();
  90. }
  91. public int AddGroup(
  92. int teamId,
  93. bool active,
  94. ClothParams.PenetrationMode mode,
  95. BezierParam distance,
  96. BezierParam radius,
  97. float maxDepth,
  98. PenetrationData[] moveLimitDataList,
  99. ReferenceDataIndex[] refDataArray,
  100. float3[] bonePenetrationDataArray
  101. )
  102. {
  103. //var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
  104. var gdata = new GroupData();
  105. gdata.teamId = teamId;
  106. gdata.active = active ? 1 : 0;
  107. gdata.mode = (int)mode;
  108. gdata.distance.Setup(distance);
  109. gdata.radius.Setup(radius);
  110. gdata.maxDepth = maxDepth;
  111. if (moveLimitDataList != null && moveLimitDataList.Length > 0)
  112. {
  113. gdata.dataChunk = dataList.AddChunk(moveLimitDataList.Length);
  114. gdata.refDataChunk = refDataList.AddChunk(refDataArray.Length);
  115. // チャンクデータコピー
  116. dataList.ToJobArray().CopyFromFast(gdata.dataChunk.startIndex, moveLimitDataList);
  117. refDataList.ToJobArray().CopyFromFast(gdata.refDataChunk.startIndex, refDataArray);
  118. }
  119. if (bonePenetrationDataArray != null && bonePenetrationDataArray.Length > 0)
  120. {
  121. gdata.bonePenetrationDataChunk = bonePenetrationDataList.AddChunk(bonePenetrationDataArray.Length);
  122. // チャンクデータコピー
  123. bonePenetrationDataList.ToJobArray().CopyFromFast(gdata.bonePenetrationDataChunk.startIndex, bonePenetrationDataArray);
  124. }
  125. int group = groupList.Add(gdata);
  126. return group;
  127. }
  128. public override void RemoveTeam(int teamId)
  129. {
  130. var teamData = MagicaPhysicsManager.Instance.Team.teamDataList[teamId];
  131. int group = teamData.penetrationGroupIndex;
  132. if (group < 0)
  133. return;
  134. var gdata = groupList[group];
  135. // チャンクデータ削除
  136. dataList.RemoveChunk(gdata.dataChunk);
  137. refDataList.RemoveChunk(gdata.refDataChunk);
  138. bonePenetrationDataList.RemoveChunk(gdata.bonePenetrationDataChunk);
  139. // データ削除
  140. groupList.Remove(group);
  141. }
  142. public void ChangeParam(int teamId, bool active, BezierParam distance, BezierParam radius, float maxDepth)
  143. {
  144. var teamData = Manager.Team.teamDataList[teamId];
  145. int group = teamData.penetrationGroupIndex;
  146. if (group < 0)
  147. return;
  148. var gdata = groupList[group];
  149. gdata.active = active ? 1 : 0;
  150. gdata.distance.Setup(distance);
  151. gdata.radius.Setup(radius);
  152. gdata.maxDepth = maxDepth;
  153. groupList[group] = gdata;
  154. }
  155. //=========================================================================================
  156. public override JobHandle SolverConstraint(int runCount, float dtime, float updatePower, int iteration, JobHandle jobHandle)
  157. {
  158. if (groupList.Count == 0)
  159. return jobHandle;
  160. // 移動制限拘束
  161. var job1 = new PenetrationJob()
  162. {
  163. runCount = runCount,
  164. groupList = groupList.ToJobArray(),
  165. dataList = dataList.ToJobArray(),
  166. refDataList = refDataList.ToJobArray(),
  167. bonePenetrationDataList = bonePenetrationDataList.ToJobArray(),
  168. flagList = Manager.Particle.flagList.ToJobArray(),
  169. teamIdList = Manager.Particle.teamIdList.ToJobArray(),
  170. nextPosList = Manager.Particle.InNextPosList.ToJobArray(),
  171. nextRotList = Manager.Particle.InNextRotList.ToJobArray(),
  172. transformIndexList = Manager.Particle.transformIndexList.ToJobArray(),
  173. depthList = Manager.Particle.depthList.ToJobArray(),
  174. basePosList = Manager.Particle.basePosList.ToJobArray(),
  175. baseRotList = Manager.Particle.baseRotList.ToJobArray(),
  176. colliderList = Manager.Team.colliderList.ToJobArray(),
  177. bonePosList = Manager.Bone.bonePosList.ToJobArray(),
  178. boneRotList = Manager.Bone.boneRotList.ToJobArray(),
  179. boneSclList = Manager.Bone.boneSclList.ToJobArray(),
  180. teamDataList = Manager.Team.teamDataList.ToJobArray(),
  181. skinningBoneList = Manager.Team.skinningBoneList.ToJobArray(),
  182. outNextPosList = Manager.Particle.OutNextPosList.ToJobArray(),
  183. posList = Manager.Particle.posList.ToJobArray(),
  184. //frictionList = Manager.Particle.frictionList.ToJobArray(),
  185. };
  186. jobHandle = job1.Schedule(Manager.Particle.Length, 64, jobHandle);
  187. Manager.Particle.SwitchingNextPosList();
  188. return jobHandle;
  189. }
  190. //=========================================================================================
  191. /// <summary>
  192. /// 浸透制限拘束ジョブ
  193. /// パーティクルごとに計算
  194. /// </summary>
  195. [BurstCompile]
  196. struct PenetrationJob : IJobParallelFor
  197. {
  198. public int runCount;
  199. [Unity.Collections.ReadOnly]
  200. public NativeArray<GroupData> groupList;
  201. [Unity.Collections.ReadOnly]
  202. public NativeArray<PenetrationData> dataList;
  203. [Unity.Collections.ReadOnly]
  204. public NativeArray<ReferenceDataIndex> refDataList;
  205. [Unity.Collections.ReadOnly]
  206. public NativeArray<float3> bonePenetrationDataList;
  207. [Unity.Collections.ReadOnly]
  208. public NativeArray<PhysicsManagerParticleData.ParticleFlag> flagList;
  209. [Unity.Collections.ReadOnly]
  210. public NativeArray<int> teamIdList;
  211. [Unity.Collections.ReadOnly]
  212. public NativeArray<float3> nextPosList;
  213. [Unity.Collections.ReadOnly]
  214. public NativeArray<quaternion> nextRotList;
  215. [Unity.Collections.ReadOnly]
  216. public NativeArray<int> transformIndexList;
  217. [Unity.Collections.ReadOnly]
  218. public NativeArray<float> depthList;
  219. [Unity.Collections.ReadOnly]
  220. public NativeArray<float3> basePosList;
  221. [Unity.Collections.ReadOnly]
  222. public NativeArray<quaternion> baseRotList;
  223. [Unity.Collections.ReadOnly]
  224. public NativeArray<int> colliderList;
  225. // bone
  226. [Unity.Collections.ReadOnly]
  227. public NativeArray<float3> bonePosList;
  228. [Unity.Collections.ReadOnly]
  229. public NativeArray<quaternion> boneRotList;
  230. [Unity.Collections.ReadOnly]
  231. public NativeArray<float3> boneSclList;
  232. // team
  233. [Unity.Collections.ReadOnly]
  234. public NativeArray<PhysicsManagerTeamData.TeamData> teamDataList;
  235. [Unity.Collections.ReadOnly]
  236. public NativeArray<int> skinningBoneList;
  237. [Unity.Collections.WriteOnly]
  238. public NativeArray<float3> outNextPosList;
  239. public NativeArray<float3> posList;
  240. //public NativeArray<float> frictionList;
  241. // パーティクルごと
  242. public void Execute(int index)
  243. {
  244. // 初期化コピー
  245. float3 nextpos = nextPosList[index];
  246. outNextPosList[index] = nextpos;
  247. var flag = flagList[index];
  248. if (flag.IsValid() == false || flag.IsFixed() || flag.IsCollider())
  249. return;
  250. // チーム
  251. var team = teamIdList[index];
  252. var teamData = teamDataList[team];
  253. if (teamData.IsActive() == false)
  254. return;
  255. if (teamData.penetrationGroupIndex < 0)
  256. return;
  257. // 更新確認
  258. if (teamData.IsUpdate(runCount) == false)
  259. return;
  260. // グループデータ
  261. var gdata = groupList[teamData.penetrationGroupIndex];
  262. if (gdata.active == 0)
  263. return;
  264. int vindex = index - teamData.particleChunk.startIndex;
  265. var oldpos = nextpos;
  266. // depth
  267. var depth = depthList[index];
  268. // move radius
  269. var moveradius = gdata.radius.Evaluate(depth);
  270. // 浸透距離
  271. float distance = gdata.distance.Evaluate(depth);
  272. // チームスケール倍率
  273. float3 scaleDirection = teamData.scaleDirection;
  274. float teamScale = teamData.scaleRatio;
  275. distance *= teamScale;
  276. moveradius *= teamScale;
  277. //Debug.Log(teamScale);
  278. // モード別処理
  279. if (gdata.mode == 0)
  280. {
  281. // Surface Penetration
  282. // データ参照情報
  283. var refdata = refDataList[gdata.refDataChunk.startIndex + vindex];
  284. if (refdata.count > 0)
  285. {
  286. // ベース位置から算出する
  287. var bpos = basePosList[index];
  288. var brot = baseRotList[index];
  289. int dindex = refdata.startIndex;
  290. var data = dataList[gdata.dataChunk.startIndex + dindex];
  291. if (data.IsValid())
  292. {
  293. //float3 n = math.mul(brot, data.localDir);
  294. float3 n = math.mul(brot, data.localDir * scaleDirection); // マイナススケール対応
  295. // 球の位置
  296. var c = bpos + n * (distance - moveradius);
  297. // 球内部制限
  298. var v = nextpos - c;
  299. var len = math.length(v);
  300. if (len > moveradius)
  301. {
  302. v *= (moveradius / len);
  303. nextpos = c + v;
  304. }
  305. }
  306. }
  307. }
  308. else if (gdata.mode == 1)
  309. {
  310. // Collider Penetration
  311. // データ参照情報
  312. var refdata = refDataList[gdata.refDataChunk.startIndex + vindex];
  313. if (refdata.count > 0)
  314. {
  315. // 球内制限
  316. float3 c = 0;
  317. int ccnt = 0;
  318. int dindex = refdata.startIndex;
  319. for (int i = 0; i < refdata.count; i++, dindex++)
  320. {
  321. var data = dataList[gdata.dataChunk.startIndex + dindex];
  322. if (data.IsValid())
  323. {
  324. int cindex = colliderList[teamData.colliderChunk.startIndex + data.colliderIndex];
  325. var cflag = flagList[cindex];
  326. if (cflag.IsValid() == false)
  327. continue;
  328. // 球内部制限
  329. c += InverseSpherePosition(ref data, teamScale, scaleDirection, distance, cindex, moveradius);
  330. ccnt++;
  331. }
  332. }
  333. if (ccnt > 0)
  334. {
  335. c /= ccnt;
  336. var opos = InverseSpherePenetration(c, moveradius, nextpos);
  337. var addv = (opos - nextpos);
  338. // stiffness test
  339. //addv *= 0.25f;
  340. // 摩擦を入れてみる
  341. //float friction = math.length(addv) * 10.0f;
  342. //frictionList[index] = math.max(friction, frictionList[index]); // 大きい方
  343. nextpos += addv;
  344. }
  345. }
  346. }
  347. else if (gdata.mode == 2)
  348. {
  349. // Bone Penetration
  350. if (depth <= gdata.maxDepth)
  351. {
  352. float3 basePos = basePosList[index];
  353. quaternion baseRot = baseRotList[index];
  354. float3 ln = bonePenetrationDataList[gdata.bonePenetrationDataChunk.startIndex + vindex];
  355. float3 n = math.mul(baseRot, ln);
  356. #if true
  357. // 球の位置
  358. var c = basePos + n * (moveradius - math.min(distance, moveradius));
  359. //var c = basePos + n * (-distance + moveradius);
  360. // 球内部制限
  361. var v = nextpos - c;
  362. var len = math.length(v);
  363. if (len > moveradius)
  364. {
  365. v *= (moveradius / len);
  366. nextpos = c + v;
  367. }
  368. #endif
  369. #if false
  370. // 平面押し出し
  371. var c = basePos - n * (distance);
  372. MathUtility.IntersectPointPlane(c, n, nextpos, out nextpos);
  373. #endif
  374. #if false
  375. // 逆バンク
  376. // 球の位置
  377. var c = basePos - n * (distance + moveradius);
  378. // 球外部制限
  379. var v = nextpos - c;
  380. var len = math.length(v);
  381. if (len < moveradius)
  382. {
  383. v *= (moveradius / len);
  384. nextpos = c + v;
  385. }
  386. #endif
  387. // test
  388. //nextpos = basePos;
  389. // 速度影響
  390. const float velocityInfluence = (1.0f - 0.3f); // 0.2f?
  391. posList[index] += (nextpos - oldpos) * velocityInfluence;
  392. }
  393. }
  394. // 書き戻し
  395. outNextPosList[index] = nextpos;
  396. }
  397. //=====================================================================================
  398. /// <summary>
  399. /// 内球制限
  400. /// </summary>
  401. /// <param name="data"></param>
  402. /// <param name="distance"></param>
  403. /// <param name="cindex"></param>
  404. /// <param name="cr"></param>
  405. /// <param name="nextpos"></param>
  406. /// <param name="outpos"></param>
  407. /// <returns></returns>
  408. /*private bool InverseSpherePenetration(ref PenetrationData data, float teamScale, float distance, int cindex, float cr, float3 nextpos, out float3 outpos)
  409. {
  410. var cpos = nextPosList[cindex];
  411. var crot = nextRotList[cindex];
  412. // スケール
  413. var tindex = transformIndexList[cindex];
  414. var cscl = boneSclList[tindex];
  415. // 中心軸
  416. var d = math.mul(crot, data.localPos * cscl) + cpos;
  417. // 方向
  418. var n = math.mul(crot, data.localDir);
  419. // 球の位置
  420. var c = d + n * (data.distance * teamScale - distance + cr);
  421. // 球内部制限
  422. var v = nextpos - c;
  423. var len = math.length(v);
  424. if (len > cr)
  425. {
  426. v *= (cr / len);
  427. outpos = c + v;
  428. return true;
  429. }
  430. else
  431. {
  432. outpos = nextpos;
  433. return false;
  434. }
  435. }*/
  436. /// <summary>
  437. /// 内球制限の中心位置を求める
  438. /// </summary>
  439. /// <param name="data"></param>
  440. /// <param name="teamScale"></param>
  441. /// <param name="distance">チームスケール済み</param>
  442. /// <param name="cindex"></param>
  443. /// <param name="cr">チームスケール済み</param>
  444. /// <returns></returns>
  445. private float3 InverseSpherePosition(ref PenetrationData data, float teamScale, float3 scaleDirection, float distance, int cindex, float cr)
  446. {
  447. var cpos = nextPosList[cindex];
  448. var crot = nextRotList[cindex];
  449. // スケール
  450. var tindex = transformIndexList[cindex];
  451. var cscl = boneSclList[tindex];
  452. // 中心軸
  453. var d = math.mul(crot, data.localPos * cscl) + cpos;
  454. // 方向
  455. //var n = math.mul(crot, data.localDir);
  456. var n = math.mul(crot, data.localDir * scaleDirection); // マイナススケール対応
  457. // 球の位置
  458. var c = d + n * (data.distance * teamScale - distance + cr);
  459. return c;
  460. }
  461. /// <summary>
  462. /// 内球移動制限をかける
  463. /// </summary>
  464. /// <param name="c"></param>
  465. /// <param name="cr"></param>
  466. /// <param name="nextpos"></param>
  467. /// <returns></returns>
  468. private float3 InverseSpherePenetration(float3 c, float cr, float3 nextpos)
  469. {
  470. // 球内部制限
  471. var v = nextpos - c;
  472. var len = math.length(v);
  473. if (len > cr)
  474. {
  475. v *= (cr / len);
  476. return c + v;
  477. }
  478. else
  479. {
  480. return nextpos;
  481. }
  482. }
  483. #if false
  484. /// <summary>
  485. /// 平面制限
  486. /// </summary>
  487. /// <param name="data"></param>
  488. /// <param name="cindex"></param>
  489. /// <param name="nextpos"></param>
  490. /// <param name="outpos"></param>
  491. /// <returns></returns>
  492. private bool PlanePenetration(ref PenetrationData data, float teamScale, float distance, int cindex, float3 nextpos, out float3 outpos)
  493. {
  494. var cpos = nextPosList[cindex];
  495. var crot = nextRotList[cindex];
  496. // スケール
  497. var tindex = transformIndexList[cindex];
  498. var cscl = boneSclList[tindex];
  499. // 中心軸
  500. var d = math.mul(crot, data.localPos * cscl) + cpos;
  501. // 方向
  502. var n = math.mul(crot, data.localDir);
  503. // 押し出し平面を求める
  504. var c = d + n * (data.distance * teamScale - distance);
  505. // c = 平面位置
  506. // n = 平面方向
  507. // 平面衝突判定と押し出し
  508. return MathUtility.IntersectPointPlane(c, n, nextpos, out outpos);
  509. }
  510. private void InversePlanePosition(ref PenetrationData data, float teamScale, float distance, int cindex, out float3 center, out float3 dir)
  511. {
  512. var cpos = nextPosList[cindex];
  513. var crot = nextRotList[cindex];
  514. // スケール
  515. var tindex = transformIndexList[cindex];
  516. var cscl = boneSclList[tindex];
  517. // 中心軸
  518. var d = math.mul(crot, data.localPos * cscl) + cpos;
  519. // 方向
  520. var n = math.mul(crot, data.localDir);
  521. // プレーン位置
  522. var c = d + n * (data.distance * teamScale - distance);
  523. center = c;
  524. dir = n;
  525. }
  526. #endif
  527. #if false
  528. /// <summary>
  529. /// 角度制限
  530. /// </summary>
  531. /// <param name="data"></param>
  532. /// <param name="cindex"></param>
  533. /// <param name="nextpos"></param>
  534. /// <param name="outpos"></param>
  535. /// <param name="ang"></param>
  536. /// <returns></returns>
  537. private bool AnglePenetration(ref PenetrationData data, int cindex, float3 nextpos, out float3 outpos, float ang)
  538. {
  539. var cpos = nextPosList[cindex];
  540. var crot = nextRotList[cindex];
  541. // スケール
  542. var tindex = transformIndexList[cindex];
  543. var cscl = boneSclList[tindex];
  544. //float scl = cscl.x; // X軸を採用(基本的には均等スケールのみを想定)
  545. // 押し出し平面を求める
  546. var c = math.mul(crot, data.localPos * cscl) + cpos;
  547. var n = math.mul(crot, data.localDir);
  548. var v = nextpos - c;
  549. float3 v2;
  550. if (MathUtility.ClampAngle(v, n, ang, out v2))
  551. {
  552. outpos = c + v2;
  553. return true;
  554. }
  555. outpos = nextpos;
  556. return false;
  557. }
  558. #endif
  559. }
  560. }
  561. }