BinBVHAnimation.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.IO;
  29. using OpenMetaverse;
  30. namespace OpenSim.Region.Framework.Scenes.Animation
  31. {
  32. /// <summary>
  33. /// Written to decode and encode a binary animation asset.
  34. /// The SecondLife Client reads in a BVH file and converts
  35. /// it to the format described here. This isn't
  36. /// </summary>
  37. public class BinBVHAnimation
  38. {
  39. /// <summary>
  40. /// Rotation Keyframe count (used internally)
  41. /// Don't use this, use the rotationkeys.Length on each joint
  42. /// </summary>
  43. private int rotationkeys;
  44. /// <summary>
  45. /// Position Keyframe count (used internally)
  46. /// Don't use this, use the positionkeys.Length on each joint
  47. /// </summary>
  48. private int positionkeys;
  49. public UInt16 unknown0; // Always 1
  50. public UInt16 unknown1; // Always 0
  51. /// <summary>
  52. /// Animation Priority
  53. /// </summary>
  54. public int Priority;
  55. /// <summary>
  56. /// The animation length in seconds.
  57. /// </summary>
  58. public Single Length;
  59. /// <summary>
  60. /// Expression set in the client. Null if [None] is selected
  61. /// </summary>
  62. public string ExpressionName; // "" (null)
  63. /// <summary>
  64. /// The time in seconds to start the animation
  65. /// </summary>
  66. public Single InPoint;
  67. /// <summary>
  68. /// The time in seconds to end the animation
  69. /// </summary>
  70. public Single OutPoint;
  71. /// <summary>
  72. /// Loop the animation
  73. /// </summary>
  74. public bool Loop;
  75. /// <summary>
  76. /// Meta data. Ease in Seconds.
  77. /// </summary>
  78. public Single EaseInTime;
  79. /// <summary>
  80. /// Meta data. Ease out seconds.
  81. /// </summary>
  82. public Single EaseOutTime;
  83. /// <summary>
  84. /// Meta Data for the Hand Pose
  85. /// </summary>
  86. public uint HandPose;
  87. /// <summary>
  88. /// Number of joints defined in the animation
  89. /// Don't use this.. use joints.Length
  90. /// </summary>
  91. private uint m_jointCount;
  92. /// <summary>
  93. /// Contains an array of joints
  94. /// </summary>
  95. public binBVHJoint[] Joints;
  96. public byte[] ToBytes()
  97. {
  98. byte[] outputbytes;
  99. using (MemoryStream ms = new MemoryStream())
  100. using (BinaryWriter iostream = new BinaryWriter(ms))
  101. {
  102. iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(unknown0)));
  103. iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(unknown1)));
  104. iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(Priority)));
  105. iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(Length)));
  106. iostream.Write(BinBVHUtil.WriteNullTerminatedString(ExpressionName));
  107. iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(InPoint)));
  108. iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(OutPoint)));
  109. iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(Loop ? 1 : 0)));
  110. iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(EaseInTime)));
  111. iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(EaseOutTime)));
  112. iostream.Write(BinBVHUtil.ES(Utils.UIntToBytes(HandPose)));
  113. iostream.Write(BinBVHUtil.ES(Utils.UIntToBytes((uint)(Joints.Length))));
  114. for (int i = 0; i < Joints.Length; i++)
  115. {
  116. Joints[i].WriteBytesToStream(iostream, InPoint, OutPoint);
  117. }
  118. iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(0)));
  119. using (MemoryStream ms2 = (MemoryStream)iostream.BaseStream)
  120. outputbytes = ms2.ToArray();
  121. }
  122. return outputbytes;
  123. }
  124. public BinBVHAnimation()
  125. {
  126. rotationkeys = 0;
  127. positionkeys = 0;
  128. unknown0 = 1;
  129. unknown1 = 0;
  130. Priority = 1;
  131. Length = 0;
  132. ExpressionName = string.Empty;
  133. InPoint = 0;
  134. OutPoint = 0;
  135. Loop = false;
  136. EaseInTime = 0;
  137. EaseOutTime = 0;
  138. HandPose = 1;
  139. m_jointCount = 0;
  140. Joints = new binBVHJoint[1];
  141. Joints[0] = new binBVHJoint();
  142. Joints[0].Name = "mPelvis";
  143. Joints[0].Priority = 7;
  144. Joints[0].positionkeys = new binBVHJointKey[1];
  145. Joints[0].rotationkeys = new binBVHJointKey[1];
  146. Joints[0].rotationkeys[0] = new binBVHJointKey();
  147. Joints[0].rotationkeys[0].time = (0f);
  148. Joints[0].rotationkeys[0].key_element.X = ((float)Random.Shared.NextDouble() * 2 - 1);
  149. Joints[0].rotationkeys[0].key_element.Y = ((float)Random.Shared.NextDouble() * 2 - 1);
  150. Joints[0].rotationkeys[0].key_element.Z = ((float)Random.Shared.NextDouble() * 2 - 1);
  151. Joints[0].positionkeys[0] = new binBVHJointKey();
  152. Joints[0].positionkeys[0].time = (0f);
  153. Joints[0].positionkeys[0].key_element.X = ((float)Random.Shared.NextDouble() * 2 - 1);
  154. Joints[0].positionkeys[0].key_element.Y = ((float)Random.Shared.NextDouble() * 2 - 1);
  155. Joints[0].positionkeys[0].key_element.Z = ((float)Random.Shared.NextDouble() * 2 - 1);
  156. }
  157. public BinBVHAnimation(byte[] animationdata)
  158. {
  159. int i = 0;
  160. if (!BitConverter.IsLittleEndian)
  161. {
  162. unknown0 = Utils.BytesToUInt16(BinBVHUtil.EndianSwap(animationdata,i,2)); i += 2; // Always 1
  163. unknown1 = Utils.BytesToUInt16(BinBVHUtil.EndianSwap(animationdata, i, 2)); i += 2; // Always 0
  164. Priority = Utils.BytesToInt(BinBVHUtil.EndianSwap(animationdata, i, 4)); i += 4;
  165. Length = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
  166. }
  167. else
  168. {
  169. unknown0 = Utils.BytesToUInt16(animationdata, i); i += 2; // Always 1
  170. unknown1 = Utils.BytesToUInt16(animationdata, i); i += 2; // Always 0
  171. Priority = Utils.BytesToInt(animationdata, i); i += 4;
  172. Length = Utils.BytesToFloat(animationdata, i); i += 4;
  173. }
  174. ExpressionName = ReadBytesUntilNull(animationdata, ref i);
  175. if (!BitConverter.IsLittleEndian)
  176. {
  177. InPoint = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
  178. OutPoint = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
  179. Loop = (Utils.BytesToInt(BinBVHUtil.EndianSwap(animationdata, i, 4)) != 0); i += 4;
  180. EaseInTime = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
  181. EaseOutTime = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
  182. HandPose = Utils.BytesToUInt(BinBVHUtil.EndianSwap(animationdata, i, 4)); i += 4; // Handpose?
  183. m_jointCount = Utils.BytesToUInt(animationdata, i); i += 4; // Get Joint count
  184. }
  185. else
  186. {
  187. InPoint = Utils.BytesToFloat(animationdata, i); i += 4;
  188. OutPoint = Utils.BytesToFloat(animationdata, i); i += 4;
  189. Loop = (Utils.BytesToInt(animationdata, i) != 0); i += 4;
  190. EaseInTime = Utils.BytesToFloat(animationdata, i); i += 4;
  191. EaseOutTime = Utils.BytesToFloat(animationdata, i); i += 4;
  192. HandPose = Utils.BytesToUInt(animationdata, i); i += 4; // Handpose?
  193. m_jointCount = Utils.BytesToUInt(animationdata, i); i += 4; // Get Joint count
  194. }
  195. Joints = new binBVHJoint[m_jointCount];
  196. // deserialize the number of joints in the animation.
  197. // Joints are variable length blocks of binary data consisting of joint data and keyframes
  198. for (int iter = 0; iter < m_jointCount; iter++)
  199. {
  200. binBVHJoint joint = readJoint(animationdata, ref i);
  201. Joints[iter] = joint;
  202. }
  203. }
  204. /// <summary>
  205. /// Variable length strings seem to be null terminated in the animation asset.. but..
  206. /// use with caution, home grown.
  207. /// advances the index.
  208. /// </summary>
  209. /// <param name="data">The animation asset byte array</param>
  210. /// <param name="i">The offset to start reading</param>
  211. /// <returns>a string</returns>
  212. private static string ReadBytesUntilNull(byte[] data, ref int i)
  213. {
  214. char nterm = '\0'; // Null terminator
  215. int endpos = i;
  216. int startpos = i;
  217. // Find the null character
  218. for (int j = i; j < data.Length; j++)
  219. {
  220. char spot = Convert.ToChar(data[j]);
  221. if (spot == nterm)
  222. {
  223. endpos = j;
  224. break;
  225. }
  226. }
  227. // if we got to the end, then it's a zero length string
  228. if (i == endpos)
  229. {
  230. // advance the 1 null character
  231. i++;
  232. return string.Empty;
  233. }
  234. else
  235. {
  236. // We found the end of the string
  237. // append the bytes from the beginning of the string to the end of the string
  238. // advance i
  239. byte[] interm = new byte[endpos-i];
  240. for (; i<endpos; i++)
  241. {
  242. interm[i-startpos] = data[i];
  243. }
  244. i++; // advance past the null character
  245. return Utils.BytesToString(interm);
  246. }
  247. }
  248. /// <summary>
  249. /// Read in a Joint from an animation asset byte array
  250. /// Variable length Joint fields, yay!
  251. /// Advances the index
  252. /// </summary>
  253. /// <param name="data">animation asset byte array</param>
  254. /// <param name="i">Byte Offset of the start of the joint</param>
  255. /// <returns>The Joint data serialized into the binBVHJoint structure</returns>
  256. private binBVHJoint readJoint(byte[] data, ref int i)
  257. {
  258. binBVHJointKey[] positions;
  259. binBVHJointKey[] rotations;
  260. binBVHJoint pJoint = new binBVHJoint();
  261. /*
  262. 109
  263. 84
  264. 111
  265. 114
  266. 114
  267. 111
  268. 0 <--- Null terminator
  269. */
  270. pJoint.Name = ReadBytesUntilNull(data, ref i); // Joint name
  271. /*
  272. 2 <- Priority Revisited
  273. 0
  274. 0
  275. 0
  276. */
  277. /*
  278. 5 <-- 5 keyframes
  279. 0
  280. 0
  281. 0
  282. ... 5 Keyframe data blocks
  283. */
  284. /*
  285. 2 <-- 2 keyframes
  286. 0
  287. 0
  288. 0
  289. .. 2 Keyframe data blocks
  290. */
  291. if (!BitConverter.IsLittleEndian)
  292. {
  293. pJoint.Priority = Utils.BytesToInt(BinBVHUtil.EndianSwap(data, i, 4)); i += 4; // Joint Priority override?
  294. rotationkeys = Utils.BytesToInt(BinBVHUtil.EndianSwap(data, i, 4)); i += 4; // How many rotation keyframes
  295. }
  296. else
  297. {
  298. pJoint.Priority = Utils.BytesToInt(data, i); i += 4; // Joint Priority override?
  299. rotationkeys = Utils.BytesToInt(data, i); i += 4; // How many rotation keyframes
  300. }
  301. // argh! floats into two bytes!.. bad bad bad bad
  302. // After fighting with it for a while.. -1, to 1 seems to give the best results
  303. rotations = readKeys(data, ref i, rotationkeys, -1f, 1f);
  304. for (int iter = 0; iter < rotations.Length; iter++)
  305. {
  306. rotations[iter].W = 1f -
  307. (rotations[iter].key_element.X + rotations[iter].key_element.Y +
  308. rotations[iter].key_element.Z);
  309. }
  310. if (!BitConverter.IsLittleEndian)
  311. {
  312. positionkeys = Utils.BytesToInt(BinBVHUtil.EndianSwap(data, i, 4)); i += 4; // How many position keyframes
  313. }
  314. else
  315. {
  316. positionkeys = Utils.BytesToInt(data, i); i += 4; // How many position keyframes
  317. }
  318. // Read in position keyframes
  319. // argh! more floats into two bytes!.. *head desk*
  320. // After fighting with it for a while.. -5, to 5 seems to give the best results
  321. positions = readKeys(data, ref i, positionkeys, -5f, 5f);
  322. pJoint.rotationkeys = rotations;
  323. pJoint.positionkeys = positions;
  324. return pJoint;
  325. }
  326. /// <summary>
  327. /// Read Keyframes of a certain type
  328. /// advance i
  329. /// </summary>
  330. /// <param name="data">Animation Byte array</param>
  331. /// <param name="i">Offset in the Byte Array. Will be advanced</param>
  332. /// <param name="keycount">Number of Keyframes</param>
  333. /// <param name="min">Scaling Min to pass to the Uint16ToFloat method</param>
  334. /// <param name="max">Scaling Max to pass to the Uint16ToFloat method</param>
  335. /// <returns></returns>
  336. private binBVHJointKey[] readKeys(byte[] data, ref int i, int keycount, float min, float max)
  337. {
  338. float x;
  339. float y;
  340. float z;
  341. /*
  342. 0.o, Float values in Two bytes.. this is just wrong >:(
  343. 17 255 <-- Time Code
  344. 17 255 <-- Time Code
  345. 255 255 <-- X
  346. 127 127 <-- X
  347. 255 255 <-- Y
  348. 127 127 <-- Y
  349. 213 213 <-- Z
  350. 142 142 <---Z
  351. */
  352. binBVHJointKey[] m_keys = new binBVHJointKey[keycount];
  353. for (int j = 0; j < keycount; j++)
  354. {
  355. binBVHJointKey pJKey = new binBVHJointKey();
  356. pJKey.time = Utils.BytesUInt16ToFloat(data, i, InPoint, OutPoint); i += 2;
  357. x = Utils.BytesUInt16ToFloat(data, i, min, max); i += 2;
  358. y = Utils.BytesUInt16ToFloat(data, i, min, max); i += 2;
  359. z = Utils.BytesUInt16ToFloat(data, i, min, max); i += 2;
  360. pJKey.key_element = new Vector3(x, y, z);
  361. m_keys[j] = pJKey;
  362. }
  363. return m_keys;
  364. }
  365. }
  366. /// <summary>
  367. /// A Joint and it's associated meta data and keyframes
  368. /// </summary>
  369. public struct binBVHJoint
  370. {
  371. /// <summary>
  372. /// Name of the Joint. Matches the avatar_skeleton.xml in client distros
  373. /// </summary>
  374. public string Name;
  375. /// <summary>
  376. /// Joint Animation Override? Was the same as the Priority in testing..
  377. /// </summary>
  378. public int Priority;
  379. /// <summary>
  380. /// Array of Rotation Keyframes in order from earliest to latest
  381. /// </summary>
  382. public binBVHJointKey[] rotationkeys;
  383. /// <summary>
  384. /// Array of Position Keyframes in order from earliest to latest
  385. /// This seems to only be for the Pelvis?
  386. /// </summary>
  387. public binBVHJointKey[] positionkeys;
  388. public void WriteBytesToStream(BinaryWriter iostream, float InPoint, float OutPoint)
  389. {
  390. iostream.Write(BinBVHUtil.WriteNullTerminatedString(Name));
  391. iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(Priority)));
  392. iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(rotationkeys.Length)));
  393. for (int i=0;i<rotationkeys.Length;i++)
  394. {
  395. rotationkeys[i].WriteBytesToStream(iostream, InPoint, OutPoint, -1f, 1f);
  396. }
  397. iostream.Write(BinBVHUtil.ES(Utils.IntToBytes((positionkeys.Length))));
  398. for (int i = 0; i < positionkeys.Length; i++)
  399. {
  400. positionkeys[i].WriteBytesToStream(iostream, InPoint, OutPoint, -256f, 256f);
  401. }
  402. }
  403. }
  404. /// <summary>
  405. /// A Joint Keyframe. This is either a position or a rotation.
  406. /// </summary>
  407. public struct binBVHJointKey
  408. {
  409. // Time in seconds for this keyframe.
  410. public float time;
  411. /// <summary>
  412. /// Either a Vector3 position or a Vector3 Euler rotation
  413. /// </summary>
  414. public Vector3 key_element;
  415. public float W;
  416. public void WriteBytesToStream(BinaryWriter iostream, float InPoint, float OutPoint, float min, float max)
  417. {
  418. iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(BinBVHUtil.FloatToUInt16(time, InPoint, OutPoint))));
  419. iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(BinBVHUtil.FloatToUInt16(key_element.X, min, max))));
  420. iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(BinBVHUtil.FloatToUInt16(key_element.Y, min, max))));
  421. iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(BinBVHUtil.FloatToUInt16(key_element.Z, min, max))));
  422. }
  423. }
  424. /// <summary>
  425. /// Poses set in the animation metadata for the hands.
  426. /// </summary>
  427. public enum HandPose : uint
  428. {
  429. Spread = 0,
  430. Relaxed = 1,
  431. Point_Both = 2,
  432. Fist = 3,
  433. Relaxed_Left = 4,
  434. Point_Left = 5,
  435. Fist_Left = 6,
  436. Relaxed_Right = 7,
  437. Point_Right = 8,
  438. Fist_Right = 9,
  439. Salute_Right = 10,
  440. Typing = 11,
  441. Peace_Right = 12
  442. }
  443. public static class BinBVHUtil
  444. {
  445. public const float ONE_OVER_U16_MAX = 1.0f / UInt16.MaxValue;
  446. public static UInt16 FloatToUInt16(float val, float lower, float upper)
  447. {
  448. UInt16 uival = 0;
  449. //m_parentGroup.GetTimeDilation() * (float)ushort.MaxValue
  450. //0-1
  451. // float difference = upper - lower;
  452. // we're trying to get a zero lower and modify all values equally so we get a percentage position
  453. if (lower > 0)
  454. {
  455. upper -= lower;
  456. val = val - lower;
  457. // start with 500 upper and 200 lower.. subtract 200 from the upper and the value
  458. }
  459. else //if (lower < 0 && upper > 0)
  460. {
  461. // double negative, 0 minus negative 5 is 5.
  462. upper += 0 - lower;
  463. lower += 0 - lower;
  464. val += 0 - lower;
  465. }
  466. if (upper == 0)
  467. val = 0;
  468. else
  469. {
  470. val /= upper;
  471. }
  472. uival = (UInt16)(val * UInt16.MaxValue);
  473. return uival;
  474. }
  475. /// <summary>
  476. /// Endian Swap
  477. /// Swaps endianness if necessary
  478. /// </summary>
  479. /// <param name="arr">Input array</param>
  480. /// <returns></returns>
  481. public static byte[] ES(byte[] arr)
  482. {
  483. if (!BitConverter.IsLittleEndian)
  484. Array.Reverse(arr);
  485. return arr;
  486. }
  487. public static byte[] EndianSwap(byte[] arr, int offset, int len)
  488. {
  489. byte[] bendian = new byte[offset + len];
  490. Buffer.BlockCopy(arr, offset, bendian, 0, len);
  491. Array.Reverse(bendian);
  492. return bendian;
  493. }
  494. public static byte[] WriteNullTerminatedString(string str)
  495. {
  496. byte[] output = new byte[str.Length + 1];
  497. Char[] chr = str.ToCharArray();
  498. int i = 0;
  499. for (i = 0; i < chr.Length; i++)
  500. {
  501. output[i] = Convert.ToByte(chr[i]);
  502. }
  503. output[i] = Convert.ToByte('\0');
  504. return output;
  505. }
  506. }
  507. }
  508. /*
  509. switch (jointname)
  510. {
  511. case "mPelvis":
  512. case "mTorso":
  513. case "mNeck":
  514. case "mHead":
  515. case "mChest":
  516. case "mHipLeft":
  517. case "mHipRight":
  518. case "mKneeLeft":
  519. case "mKneeRight":
  520. // XYZ->ZXY
  521. t = x;
  522. x = y;
  523. y = t;
  524. break;
  525. case "mCollarLeft":
  526. case "mCollarRight":
  527. case "mElbowLeft":
  528. case "mElbowRight":
  529. // YZX ->ZXY
  530. t = z;
  531. z = x;
  532. x = y;
  533. y = t;
  534. break;
  535. case "mWristLeft":
  536. case "mWristRight":
  537. case "mShoulderLeft":
  538. case "mShoulderRight":
  539. // ZYX->ZXY
  540. t = y;
  541. y = z;
  542. z = t;
  543. break;
  544. case "mAnkleLeft":
  545. case "mAnkleRight":
  546. // XYZ ->ZXY
  547. t = x;
  548. x = z;
  549. z = y;
  550. y = t;
  551. break;
  552. }
  553. */