DynamicBone.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. [AddComponentMenu("Dynamic Bone/Dynamic Bone")]
  4. public class DynamicBone : MonoBehaviour
  5. {
  6. #if UNITY_5_3_OR_NEWER
  7. [Tooltip("The root of the transform hierarchy to apply physics.")]
  8. #endif
  9. public Transform m_Root = null;
  10. #if UNITY_5_3_OR_NEWER
  11. [Tooltip("Internal physics simulation rate.")]
  12. #endif
  13. public float m_UpdateRate = 60.0f;
  14. public enum UpdateMode
  15. {
  16. Normal,
  17. AnimatePhysics,
  18. UnscaledTime,
  19. Default
  20. }
  21. public UpdateMode m_UpdateMode = UpdateMode.Default;
  22. #if UNITY_5_3_OR_NEWER
  23. [Tooltip("How much the bones slowed down.")]
  24. #endif
  25. [Range(0, 1)]
  26. public float m_Damping = 0.1f;
  27. public AnimationCurve m_DampingDistrib = null;
  28. #if UNITY_5_3_OR_NEWER
  29. [Tooltip("How much the force applied to return each bone to original orientation.")]
  30. #endif
  31. [Range(0, 1)]
  32. public float m_Elasticity = 0.1f;
  33. public AnimationCurve m_ElasticityDistrib = null;
  34. #if UNITY_5_3_OR_NEWER
  35. [Tooltip("How much bone's original orientation are preserved.")]
  36. #endif
  37. [Range(0, 1)]
  38. public float m_Stiffness = 0.1f;
  39. public AnimationCurve m_StiffnessDistrib = null;
  40. #if UNITY_5_3_OR_NEWER
  41. [Tooltip("How much character's position change is ignored in physics simulation.")]
  42. #endif
  43. [Range(0, 1)]
  44. public float m_Inert = 0;
  45. public AnimationCurve m_InertDistrib = null;
  46. #if UNITY_5_3_OR_NEWER
  47. [Tooltip("How much the bones slowed down when collide.")]
  48. #endif
  49. public float m_Friction = 0;
  50. public AnimationCurve m_FrictionDistrib = null;
  51. #if UNITY_5_3_OR_NEWER
  52. [Tooltip("Each bone can be a sphere to collide with colliders. Radius describe sphere's size.")]
  53. #endif
  54. public float m_Radius = 0;
  55. public AnimationCurve m_RadiusDistrib = null;
  56. #if UNITY_5_3_OR_NEWER
  57. [Tooltip("If End Length is not zero, an extra bone is generated at the end of transform hierarchy.")]
  58. #endif
  59. public float m_EndLength = 0;
  60. #if UNITY_5_3_OR_NEWER
  61. [Tooltip("If End Offset is not zero, an extra bone is generated at the end of transform hierarchy.")]
  62. #endif
  63. public Vector3 m_EndOffset = Vector3.zero;
  64. #if UNITY_5_3_OR_NEWER
  65. [Tooltip("The force apply to bones. Partial force apply to character's initial pose is cancelled out.")]
  66. #endif
  67. public Vector3 m_Gravity = Vector3.zero;
  68. #if UNITY_5_3_OR_NEWER
  69. [Tooltip("The force apply to bones.")]
  70. #endif
  71. public Vector3 m_Force = Vector3.zero;
  72. #if UNITY_5_3_OR_NEWER
  73. [Tooltip("Collider objects interact with the bones.")]
  74. #endif
  75. public List<DynamicBoneColliderBase> m_Colliders = null;
  76. #if UNITY_5_3_OR_NEWER
  77. [Tooltip("Bones exclude from physics simulation.")]
  78. #endif
  79. public List<Transform> m_Exclusions = null;
  80. public enum FreezeAxis
  81. {
  82. None, X, Y, Z
  83. }
  84. #if UNITY_5_3_OR_NEWER
  85. [Tooltip("Constrain bones to move on specified plane.")]
  86. #endif
  87. public FreezeAxis m_FreezeAxis = FreezeAxis.None;
  88. #if UNITY_5_3_OR_NEWER
  89. [Tooltip("Disable physics simulation automatically if character is far from camera or player.")]
  90. #endif
  91. public bool m_DistantDisable = false;
  92. public Transform m_ReferenceObject = null;
  93. public float m_DistanceToObject = 20;
  94. Vector3 m_LocalGravity = Vector3.zero;
  95. Vector3 m_ObjectMove = Vector3.zero;
  96. Vector3 m_ObjectPrevPosition = Vector3.zero;
  97. float m_BoneTotalLength = 0;
  98. float m_ObjectScale = 1.0f;
  99. float m_Time = 0;
  100. float m_Weight = 1.0f;
  101. bool m_DistantDisabled = false;
  102. class Particle
  103. {
  104. public Transform m_Transform = null;
  105. public int m_ParentIndex = -1;
  106. public float m_Damping = 0;
  107. public float m_Elasticity = 0;
  108. public float m_Stiffness = 0;
  109. public float m_Inert = 0;
  110. public float m_Friction = 0;
  111. public float m_Radius = 0;
  112. public float m_BoneLength = 0;
  113. public bool m_isCollide = false;
  114. public Vector3 m_Position = Vector3.zero;
  115. public Vector3 m_PrevPosition = Vector3.zero;
  116. public Vector3 m_EndOffset = Vector3.zero;
  117. public Vector3 m_InitLocalPosition = Vector3.zero;
  118. public Quaternion m_InitLocalRotation = Quaternion.identity;
  119. }
  120. List<Particle> m_Particles = new List<Particle>();
  121. void Start()
  122. {
  123. SetupParticles();
  124. }
  125. void FixedUpdate()
  126. {
  127. if (m_UpdateMode == UpdateMode.AnimatePhysics)
  128. PreUpdate();
  129. }
  130. void Update()
  131. {
  132. if (m_UpdateMode != UpdateMode.AnimatePhysics)
  133. PreUpdate();
  134. }
  135. void LateUpdate()
  136. {
  137. if (m_DistantDisable)
  138. CheckDistance();
  139. if (m_Weight > 0 && !(m_DistantDisable && m_DistantDisabled))
  140. {
  141. #if UNITY_5_3_OR_NEWER
  142. float dt = m_UpdateMode == UpdateMode.UnscaledTime ? Time.unscaledDeltaTime : Time.deltaTime;
  143. #else
  144. float dt = Time.deltaTime;
  145. #endif
  146. UpdateDynamicBones(dt);
  147. }
  148. }
  149. void PreUpdate()
  150. {
  151. if (m_Weight > 0 && !(m_DistantDisable && m_DistantDisabled))
  152. InitTransforms();
  153. }
  154. void CheckDistance()
  155. {
  156. Transform rt = m_ReferenceObject;
  157. if (rt == null && Camera.main != null)
  158. rt = Camera.main.transform;
  159. if (rt != null)
  160. {
  161. float d = (rt.position - transform.position).sqrMagnitude;
  162. bool disable = d > m_DistanceToObject * m_DistanceToObject;
  163. if (disable != m_DistantDisabled)
  164. {
  165. if (!disable)
  166. ResetParticlesPosition();
  167. m_DistantDisabled = disable;
  168. }
  169. }
  170. }
  171. void OnEnable()
  172. {
  173. ResetParticlesPosition();
  174. }
  175. void OnDisable()
  176. {
  177. InitTransforms();
  178. }
  179. void OnValidate()
  180. {
  181. m_UpdateRate = Mathf.Max(m_UpdateRate, 0);
  182. m_Damping = Mathf.Clamp01(m_Damping);
  183. m_Elasticity = Mathf.Clamp01(m_Elasticity);
  184. m_Stiffness = Mathf.Clamp01(m_Stiffness);
  185. m_Inert = Mathf.Clamp01(m_Inert);
  186. m_Friction = Mathf.Clamp01(m_Friction);
  187. m_Radius = Mathf.Max(m_Radius, 0);
  188. if (Application.isEditor && Application.isPlaying)
  189. {
  190. InitTransforms();
  191. SetupParticles();
  192. }
  193. }
  194. void OnDrawGizmosSelected()
  195. {
  196. if (!enabled || m_Root == null)
  197. return;
  198. if (Application.isEditor && !Application.isPlaying && transform.hasChanged)
  199. {
  200. InitTransforms();
  201. SetupParticles();
  202. }
  203. Gizmos.color = Color.white;
  204. for (int i = 0; i < m_Particles.Count; ++i)
  205. {
  206. Particle p = m_Particles[i];
  207. if (p.m_ParentIndex >= 0)
  208. {
  209. Particle p0 = m_Particles[p.m_ParentIndex];
  210. Gizmos.DrawLine(p.m_Position, p0.m_Position);
  211. }
  212. if (p.m_Radius > 0)
  213. Gizmos.DrawWireSphere(p.m_Position, p.m_Radius * m_ObjectScale);
  214. }
  215. }
  216. public void SetWeight(float w)
  217. {
  218. if (m_Weight != w)
  219. {
  220. if (w == 0)
  221. InitTransforms();
  222. else if (m_Weight == 0)
  223. ResetParticlesPosition();
  224. m_Weight = w;
  225. }
  226. }
  227. public float GetWeight()
  228. {
  229. return m_Weight;
  230. }
  231. void UpdateDynamicBones(float t)
  232. {
  233. if (m_Root == null)
  234. return;
  235. m_ObjectScale = Mathf.Abs(transform.lossyScale.x);
  236. m_ObjectMove = transform.position - m_ObjectPrevPosition;
  237. m_ObjectPrevPosition = transform.position;
  238. int loop = 1;
  239. float timeVar = 1;
  240. if (m_UpdateMode == UpdateMode.Default)
  241. {
  242. if (m_UpdateRate > 0)
  243. {
  244. timeVar = Time.deltaTime * m_UpdateRate;
  245. }
  246. else
  247. {
  248. timeVar = Time.deltaTime;
  249. }
  250. }
  251. else
  252. {
  253. if (m_UpdateRate > 0)
  254. {
  255. float dt = 1.0f / m_UpdateRate;
  256. m_Time += t;
  257. loop = 0;
  258. while (m_Time >= dt)
  259. {
  260. m_Time -= dt;
  261. if (++loop >= 3)
  262. {
  263. m_Time = 0;
  264. break;
  265. }
  266. }
  267. }
  268. }
  269. if (loop > 0)
  270. {
  271. for (int i = 0; i < loop; ++i)
  272. {
  273. UpdateParticles1(timeVar);
  274. UpdateParticles2(timeVar);
  275. m_ObjectMove = Vector3.zero;
  276. }
  277. }
  278. else
  279. {
  280. SkipUpdateParticles();
  281. }
  282. ApplyParticlesToTransforms();
  283. }
  284. public void SetupParticles()
  285. {
  286. m_Particles.Clear();
  287. if (m_Root == null)
  288. return;
  289. m_LocalGravity = m_Root.InverseTransformDirection(m_Gravity);
  290. m_ObjectScale = Mathf.Abs(transform.lossyScale.x);
  291. m_ObjectPrevPosition = transform.position;
  292. m_ObjectMove = Vector3.zero;
  293. m_BoneTotalLength = 0;
  294. AppendParticles(m_Root, -1, 0);
  295. UpdateParameters();
  296. }
  297. void AppendParticles(Transform b, int parentIndex, float boneLength)
  298. {
  299. Particle p = new Particle();
  300. p.m_Transform = b;
  301. p.m_ParentIndex = parentIndex;
  302. if (b != null)
  303. {
  304. p.m_Position = p.m_PrevPosition = b.position;
  305. p.m_InitLocalPosition = b.localPosition;
  306. p.m_InitLocalRotation = b.localRotation;
  307. }
  308. else // end bone
  309. {
  310. Transform pb = m_Particles[parentIndex].m_Transform;
  311. if (m_EndLength > 0)
  312. {
  313. Transform ppb = pb.parent;
  314. if (ppb != null)
  315. p.m_EndOffset = pb.InverseTransformPoint((pb.position * 2 - ppb.position)) * m_EndLength;
  316. else
  317. p.m_EndOffset = new Vector3(m_EndLength, 0, 0);
  318. }
  319. else
  320. {
  321. p.m_EndOffset = pb.InverseTransformPoint(transform.TransformDirection(m_EndOffset) + pb.position);
  322. }
  323. p.m_Position = p.m_PrevPosition = pb.TransformPoint(p.m_EndOffset);
  324. }
  325. if (parentIndex >= 0)
  326. {
  327. boneLength += (m_Particles[parentIndex].m_Transform.position - p.m_Position).magnitude;
  328. p.m_BoneLength = boneLength;
  329. m_BoneTotalLength = Mathf.Max(m_BoneTotalLength, boneLength);
  330. }
  331. int index = m_Particles.Count;
  332. m_Particles.Add(p);
  333. if (b != null)
  334. {
  335. for (int i = 0; i < b.childCount; ++i)
  336. {
  337. Transform child = b.GetChild(i);
  338. bool exclude = false;
  339. if (m_Exclusions != null)
  340. {
  341. exclude = m_Exclusions.Contains(child);
  342. }
  343. if (!exclude)
  344. AppendParticles(child, index, boneLength);
  345. else if (m_EndLength > 0 || m_EndOffset != Vector3.zero)
  346. AppendParticles(null, index, boneLength);
  347. }
  348. if (b.childCount == 0 && (m_EndLength > 0 || m_EndOffset != Vector3.zero))
  349. AppendParticles(null, index, boneLength);
  350. }
  351. }
  352. public void UpdateParameters()
  353. {
  354. if (m_Root == null)
  355. return;
  356. m_LocalGravity = m_Root.InverseTransformDirection(m_Gravity);
  357. for (int i = 0; i < m_Particles.Count; ++i)
  358. {
  359. Particle p = m_Particles[i];
  360. p.m_Damping = m_Damping;
  361. p.m_Elasticity = m_Elasticity;
  362. p.m_Stiffness = m_Stiffness;
  363. p.m_Inert = m_Inert;
  364. p.m_Friction = m_Friction;
  365. p.m_Radius = m_Radius;
  366. if (m_BoneTotalLength > 0)
  367. {
  368. float a = p.m_BoneLength / m_BoneTotalLength;
  369. if (m_DampingDistrib != null && m_DampingDistrib.keys.Length > 0)
  370. p.m_Damping *= m_DampingDistrib.Evaluate(a);
  371. if (m_ElasticityDistrib != null && m_ElasticityDistrib.keys.Length > 0)
  372. p.m_Elasticity *= m_ElasticityDistrib.Evaluate(a);
  373. if (m_StiffnessDistrib != null && m_StiffnessDistrib.keys.Length > 0)
  374. p.m_Stiffness *= m_StiffnessDistrib.Evaluate(a);
  375. if (m_InertDistrib != null && m_InertDistrib.keys.Length > 0)
  376. p.m_Inert *= m_InertDistrib.Evaluate(a);
  377. if (m_FrictionDistrib != null && m_FrictionDistrib.keys.Length > 0)
  378. p.m_Friction *= m_FrictionDistrib.Evaluate(a);
  379. if (m_RadiusDistrib != null && m_RadiusDistrib.keys.Length > 0)
  380. p.m_Radius *= m_RadiusDistrib.Evaluate(a);
  381. }
  382. p.m_Damping = Mathf.Clamp01(p.m_Damping);
  383. p.m_Elasticity = Mathf.Clamp01(p.m_Elasticity);
  384. p.m_Stiffness = Mathf.Clamp01(p.m_Stiffness);
  385. p.m_Inert = Mathf.Clamp01(p.m_Inert);
  386. p.m_Friction = Mathf.Clamp01(p.m_Friction);
  387. p.m_Radius = Mathf.Max(p.m_Radius, 0);
  388. }
  389. }
  390. void InitTransforms()
  391. {
  392. for (int i = 0; i < m_Particles.Count; ++i)
  393. {
  394. Particle p = m_Particles[i];
  395. if (p.m_Transform != null)
  396. {
  397. p.m_Transform.localPosition = p.m_InitLocalPosition;
  398. p.m_Transform.localRotation = p.m_InitLocalRotation;
  399. }
  400. }
  401. }
  402. void ResetParticlesPosition()
  403. {
  404. for (int i = 0; i < m_Particles.Count; ++i)
  405. {
  406. Particle p = m_Particles[i];
  407. if (p.m_Transform != null)
  408. {
  409. p.m_Position = p.m_PrevPosition = p.m_Transform.position;
  410. }
  411. else // end bone
  412. {
  413. Transform pb = m_Particles[p.m_ParentIndex].m_Transform;
  414. p.m_Position = p.m_PrevPosition = pb.TransformPoint(p.m_EndOffset);
  415. }
  416. p.m_isCollide = false;
  417. }
  418. m_ObjectPrevPosition = transform.position;
  419. }
  420. void UpdateParticles1(float timeVar)
  421. {
  422. Vector3 force = m_Gravity;
  423. Vector3 fdir = m_Gravity.normalized;
  424. Vector3 rf = m_Root.TransformDirection(m_LocalGravity);
  425. Vector3 pf = fdir * Mathf.Max(Vector3.Dot(rf, fdir), 0); // project current gravity to rest gravity
  426. force -= pf; // remove projected gravity
  427. force = (force + m_Force) * (m_ObjectScale * timeVar);
  428. for (int i = 0; i < m_Particles.Count; ++i)
  429. {
  430. Particle p = m_Particles[i];
  431. if (p.m_ParentIndex >= 0)
  432. {
  433. // verlet integration
  434. Vector3 v = p.m_Position - p.m_PrevPosition;
  435. Vector3 rmove = m_ObjectMove * p.m_Inert;
  436. p.m_PrevPosition = p.m_Position + rmove;
  437. float damping = p.m_Damping;
  438. if (p.m_isCollide)
  439. {
  440. damping += p.m_Friction;
  441. if (damping > 1)
  442. damping = 1;
  443. p.m_isCollide = false;
  444. }
  445. p.m_Position += v * (1 - damping) + force + rmove;
  446. }
  447. else
  448. {
  449. p.m_PrevPosition = p.m_Position;
  450. p.m_Position = p.m_Transform.position;
  451. }
  452. }
  453. }
  454. void UpdateParticles2(float timeVar)
  455. {
  456. Plane movePlane = new Plane();
  457. for (int i = 1; i < m_Particles.Count; ++i)
  458. {
  459. Particle p = m_Particles[i];
  460. Particle p0 = m_Particles[p.m_ParentIndex];
  461. float restLen;
  462. if (p.m_Transform != null)
  463. restLen = (p0.m_Transform.position - p.m_Transform.position).magnitude;
  464. else
  465. restLen = p0.m_Transform.localToWorldMatrix.MultiplyVector(p.m_EndOffset).magnitude;
  466. // keep shape
  467. float stiffness = Mathf.Lerp(1.0f, p.m_Stiffness, m_Weight);
  468. if (stiffness > 0 || p.m_Elasticity > 0)
  469. {
  470. Matrix4x4 m0 = p0.m_Transform.localToWorldMatrix;
  471. m0.SetColumn(3, p0.m_Position);
  472. Vector3 restPos;
  473. if (p.m_Transform != null)
  474. restPos = m0.MultiplyPoint3x4(p.m_Transform.localPosition);
  475. else
  476. restPos = m0.MultiplyPoint3x4(p.m_EndOffset);
  477. Vector3 d = restPos - p.m_Position;
  478. p.m_Position += d * (p.m_Elasticity * timeVar);
  479. if (stiffness > 0)
  480. {
  481. d = restPos - p.m_Position;
  482. float len = d.magnitude;
  483. float maxlen = restLen * (1 - stiffness) * 2;
  484. if (len > maxlen)
  485. p.m_Position += d * ((len - maxlen) / len);
  486. }
  487. }
  488. // collide
  489. if (m_Colliders != null)
  490. {
  491. float particleRadius = p.m_Radius * m_ObjectScale;
  492. for (int j = 0; j < m_Colliders.Count; ++j)
  493. {
  494. DynamicBoneColliderBase c = m_Colliders[j];
  495. if (c != null && c.enabled)
  496. p.m_isCollide |= c.Collide(ref p.m_Position, particleRadius);
  497. }
  498. }
  499. // freeze axis, project to plane
  500. if (m_FreezeAxis != FreezeAxis.None)
  501. {
  502. switch (m_FreezeAxis)
  503. {
  504. case FreezeAxis.X:
  505. movePlane.SetNormalAndPosition(p0.m_Transform.right, p0.m_Position);
  506. break;
  507. case FreezeAxis.Y:
  508. movePlane.SetNormalAndPosition(p0.m_Transform.up, p0.m_Position);
  509. break;
  510. case FreezeAxis.Z:
  511. movePlane.SetNormalAndPosition(p0.m_Transform.forward, p0.m_Position);
  512. break;
  513. }
  514. p.m_Position -= movePlane.normal * movePlane.GetDistanceToPoint(p.m_Position);
  515. }
  516. // keep length
  517. Vector3 dd = p0.m_Position - p.m_Position;
  518. float leng = dd.magnitude;
  519. if (leng > 0)
  520. p.m_Position += dd * ((leng - restLen) / leng);
  521. }
  522. }
  523. // only update stiffness and keep bone length
  524. void SkipUpdateParticles()
  525. {
  526. for (int i = 0; i < m_Particles.Count; ++i)
  527. {
  528. Particle p = m_Particles[i];
  529. if (p.m_ParentIndex >= 0)
  530. {
  531. p.m_PrevPosition += m_ObjectMove;
  532. p.m_Position += m_ObjectMove;
  533. Particle p0 = m_Particles[p.m_ParentIndex];
  534. float restLen;
  535. if (p.m_Transform != null)
  536. restLen = (p0.m_Transform.position - p.m_Transform.position).magnitude;
  537. else
  538. restLen = p0.m_Transform.localToWorldMatrix.MultiplyVector(p.m_EndOffset).magnitude;
  539. // keep shape
  540. float stiffness = Mathf.Lerp(1.0f, p.m_Stiffness, m_Weight);
  541. if (stiffness > 0)
  542. {
  543. Matrix4x4 m0 = p0.m_Transform.localToWorldMatrix;
  544. m0.SetColumn(3, p0.m_Position);
  545. Vector3 restPos;
  546. if (p.m_Transform != null)
  547. restPos = m0.MultiplyPoint3x4(p.m_Transform.localPosition);
  548. else
  549. restPos = m0.MultiplyPoint3x4(p.m_EndOffset);
  550. Vector3 d = restPos - p.m_Position;
  551. float len = d.magnitude;
  552. float maxlen = restLen * (1 - stiffness) * 2;
  553. if (len > maxlen)
  554. p.m_Position += d * ((len - maxlen) / len);
  555. }
  556. // keep length
  557. Vector3 dd = p0.m_Position - p.m_Position;
  558. float leng = dd.magnitude;
  559. if (leng > 0)
  560. p.m_Position += dd * ((leng - restLen) / leng);
  561. }
  562. else
  563. {
  564. p.m_PrevPosition = p.m_Position;
  565. p.m_Position = p.m_Transform.position;
  566. }
  567. }
  568. }
  569. static Vector3 MirrorVector(Vector3 v, Vector3 axis)
  570. {
  571. return v - axis * (Vector3.Dot(v, axis) * 2);
  572. }
  573. void ApplyParticlesToTransforms()
  574. {
  575. #if !UNITY_5_4_OR_NEWER
  576. // detect negative scale
  577. Vector3 ax = Vector3.right;
  578. Vector3 ay = Vector3.up;
  579. Vector3 az = Vector3.forward;
  580. bool nx = false, ny = false, nz = false;
  581. Vector3 loosyScale = transform.lossyScale;
  582. if (loosyScale.x < 0 || loosyScale.y < 0 || loosyScale.z < 0)
  583. {
  584. Transform mirrorObject = transform;
  585. do
  586. {
  587. Vector3 ls = mirrorObject.localScale;
  588. nx = ls.x < 0;
  589. if (nx)
  590. ax = mirrorObject.right;
  591. ny = ls.y < 0;
  592. if (ny)
  593. ay = mirrorObject.up;
  594. nz = ls.z < 0;
  595. if (nz)
  596. az = mirrorObject.forward;
  597. if (nx || ny || nz)
  598. break;
  599. mirrorObject = mirrorObject.parent;
  600. }
  601. while (mirrorObject != null);
  602. }
  603. #endif
  604. for (int i = 1; i < m_Particles.Count; ++i)
  605. {
  606. Particle p = m_Particles[i];
  607. Particle p0 = m_Particles[p.m_ParentIndex];
  608. if (p0.m_Transform.childCount <= 1) // do not modify bone orientation if has more then one child
  609. {
  610. Vector3 v;
  611. if (p.m_Transform != null)
  612. v = p.m_Transform.localPosition;
  613. else
  614. v = p.m_EndOffset;
  615. Vector3 v2 = p.m_Position - p0.m_Position;
  616. #if !UNITY_5_4_OR_NEWER
  617. if (nx)
  618. v2 = MirrorVector(v2, ax);
  619. if (ny)
  620. v2 = MirrorVector(v2, ay);
  621. if (nz)
  622. v2 = MirrorVector(v2, az);
  623. #endif
  624. Quaternion rot = Quaternion.FromToRotation(p0.m_Transform.TransformDirection(v), v2);
  625. p0.m_Transform.rotation = rot * p0.m_Transform.rotation;
  626. }
  627. if (p.m_Transform != null)
  628. p.m_Transform.position = p.m_Position;
  629. }
  630. }
  631. }