OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
BinBVHAnimation.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.IO;
30 using OpenMetaverse;
31 
32 namespace OpenSim.Region.Framework.Scenes.Animation
33 {
39  public class BinBVHAnimation
40  {
45  private int rotationkeys;
46 
51  private int positionkeys;
52 
53  public UInt16 unknown0; // Always 1
54  public UInt16 unknown1; // Always 0
55 
59  public int Priority;
60 
64  public Single Length;
65 
69  public string ExpressionName; // "" (null)
70 
74  public Single InPoint;
75 
79  public Single OutPoint;
80 
84  public bool Loop;
85 
89  public Single EaseInTime;
90 
94  public Single EaseOutTime;
95 
99  public uint HandPose;
100 
105  private uint m_jointCount;
106 
107 
111  public binBVHJoint[] Joints;
112 
113 
114  public byte[] ToBytes()
115  {
116  byte[] outputbytes;
117 
118  using (MemoryStream ms = new MemoryStream())
119  using (BinaryWriter iostream = new BinaryWriter(ms))
120  {
121  iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(unknown0)));
122  iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(unknown1)));
123  iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(Priority)));
124  iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(Length)));
125  iostream.Write(BinBVHUtil.WriteNullTerminatedString(ExpressionName));
126  iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(InPoint)));
127  iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(OutPoint)));
128  iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(Loop ? 1 : 0)));
129  iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(EaseInTime)));
130  iostream.Write(BinBVHUtil.ES(Utils.FloatToBytes(EaseOutTime)));
131  iostream.Write(BinBVHUtil.ES(Utils.UIntToBytes(HandPose)));
132  iostream.Write(BinBVHUtil.ES(Utils.UIntToBytes((uint)(Joints.Length))));
133 
134  for (int i = 0; i < Joints.Length; i++)
135  {
136  Joints[i].WriteBytesToStream(iostream, InPoint, OutPoint);
137  }
138  iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(0)));
139 
140  using (MemoryStream ms2 = (MemoryStream)iostream.BaseStream)
141  outputbytes = ms2.ToArray();
142  }
143 
144  return outputbytes;
145  }
146 
148  {
149  rotationkeys = 0;
150  positionkeys = 0;
151  unknown0 = 1;
152  unknown1 = 0;
153  Priority = 1;
154  Length = 0;
155  ExpressionName = string.Empty;
156  InPoint = 0;
157  OutPoint = 0;
158  Loop = false;
159  EaseInTime = 0;
160  EaseOutTime = 0;
161  HandPose = 1;
162  m_jointCount = 0;
163 
164  Joints = new binBVHJoint[1];
165  Joints[0] = new binBVHJoint();
166  Joints[0].Name = "mPelvis";
167  Joints[0].Priority = 7;
168  Joints[0].positionkeys = new binBVHJointKey[1];
169  Joints[0].rotationkeys = new binBVHJointKey[1];
170  Random rnd = new Random();
171 
172  Joints[0].rotationkeys[0] = new binBVHJointKey();
173  Joints[0].rotationkeys[0].time = (0f);
174  Joints[0].rotationkeys[0].key_element.X = ((float)rnd.NextDouble() * 2 - 1);
175  Joints[0].rotationkeys[0].key_element.Y = ((float)rnd.NextDouble() * 2 - 1);
176  Joints[0].rotationkeys[0].key_element.Z = ((float)rnd.NextDouble() * 2 - 1);
177 
178  Joints[0].positionkeys[0] = new binBVHJointKey();
179  Joints[0].positionkeys[0].time = (0f);
180  Joints[0].positionkeys[0].key_element.X = ((float)rnd.NextDouble() * 2 - 1);
181  Joints[0].positionkeys[0].key_element.Y = ((float)rnd.NextDouble() * 2 - 1);
182  Joints[0].positionkeys[0].key_element.Z = ((float)rnd.NextDouble() * 2 - 1);
183 
184 
185  }
186 
187  public BinBVHAnimation(byte[] animationdata)
188  {
189  int i = 0;
190  if (!BitConverter.IsLittleEndian)
191  {
192  unknown0 = Utils.BytesToUInt16(BinBVHUtil.EndianSwap(animationdata,i,2)); i += 2; // Always 1
193  unknown1 = Utils.BytesToUInt16(BinBVHUtil.EndianSwap(animationdata, i, 2)); i += 2; // Always 0
194  Priority = Utils.BytesToInt(BinBVHUtil.EndianSwap(animationdata, i, 4)); i += 4;
195  Length = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
196  }
197  else
198  {
199  unknown0 = Utils.BytesToUInt16(animationdata, i); i += 2; // Always 1
200  unknown1 = Utils.BytesToUInt16(animationdata, i); i += 2; // Always 0
201  Priority = Utils.BytesToInt(animationdata, i); i += 4;
202  Length = Utils.BytesToFloat(animationdata, i); i += 4;
203  }
204  ExpressionName = ReadBytesUntilNull(animationdata, ref i);
205  if (!BitConverter.IsLittleEndian)
206  {
207  InPoint = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
208  OutPoint = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
209  Loop = (Utils.BytesToInt(BinBVHUtil.EndianSwap(animationdata, i, 4)) != 0); i += 4;
210  EaseInTime = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
211  EaseOutTime = Utils.BytesToFloat(BinBVHUtil.EndianSwap(animationdata, i, 4), 0); i += 4;
212  HandPose = Utils.BytesToUInt(BinBVHUtil.EndianSwap(animationdata, i, 4)); i += 4; // Handpose?
213 
214  m_jointCount = Utils.BytesToUInt(animationdata, i); i += 4; // Get Joint count
215  }
216  else
217  {
218  InPoint = Utils.BytesToFloat(animationdata, i); i += 4;
219  OutPoint = Utils.BytesToFloat(animationdata, i); i += 4;
220  Loop = (Utils.BytesToInt(animationdata, i) != 0); i += 4;
221  EaseInTime = Utils.BytesToFloat(animationdata, i); i += 4;
222  EaseOutTime = Utils.BytesToFloat(animationdata, i); i += 4;
223  HandPose = Utils.BytesToUInt(animationdata, i); i += 4; // Handpose?
224 
225  m_jointCount = Utils.BytesToUInt(animationdata, i); i += 4; // Get Joint count
226  }
227  Joints = new binBVHJoint[m_jointCount];
228 
229  // deserialize the number of joints in the animation.
230  // Joints are variable length blocks of binary data consisting of joint data and keyframes
231  for (int iter = 0; iter < m_jointCount; iter++)
232  {
233  binBVHJoint joint = readJoint(animationdata, ref i);
234  Joints[iter] = joint;
235  }
236  }
237 
238 
247  private static string ReadBytesUntilNull(byte[] data, ref int i)
248  {
249  char nterm = '\0'; // Null terminator
250  int endpos = i;
251  int startpos = i;
252 
253  // Find the null character
254  for (int j = i; j < data.Length; j++)
255  {
256  char spot = Convert.ToChar(data[j]);
257  if (spot == nterm)
258  {
259  endpos = j;
260  break;
261  }
262  }
263 
264  // if we got to the end, then it's a zero length string
265  if (i == endpos)
266  {
267  // advance the 1 null character
268  i++;
269  return string.Empty;
270  }
271  else
272  {
273  // We found the end of the string
274  // append the bytes from the beginning of the string to the end of the string
275  // advance i
276  byte[] interm = new byte[endpos-i];
277  for (; i<endpos; i++)
278  {
279  interm[i-startpos] = data[i];
280  }
281  i++; // advance past the null character
282 
283  return Utils.BytesToString(interm);
284  }
285  }
286 
295  private binBVHJoint readJoint(byte[] data, ref int i)
296  {
297 
298  binBVHJointKey[] positions;
299  binBVHJointKey[] rotations;
300 
301  binBVHJoint pJoint = new binBVHJoint();
302 
303  /*
304  109
305  84
306  111
307  114
308  114
309  111
310  0 <--- Null terminator
311  */
312 
313  pJoint.Name = ReadBytesUntilNull(data, ref i); // Joint name
314 
315  /*
316  2 <- Priority Revisited
317  0
318  0
319  0
320  */
321 
322  /*
323  5 <-- 5 keyframes
324  0
325  0
326  0
327  ... 5 Keyframe data blocks
328  */
329 
330  /*
331  2 <-- 2 keyframes
332  0
333  0
334  0
335  .. 2 Keyframe data blocks
336  */
337  if (!BitConverter.IsLittleEndian)
338  {
339  pJoint.Priority = Utils.BytesToInt(BinBVHUtil.EndianSwap(data, i, 4)); i += 4; // Joint Priority override?
340  rotationkeys = Utils.BytesToInt(BinBVHUtil.EndianSwap(data, i, 4)); i += 4; // How many rotation keyframes
341  }
342  else
343  {
344  pJoint.Priority = Utils.BytesToInt(data, i); i += 4; // Joint Priority override?
345  rotationkeys = Utils.BytesToInt(data, i); i += 4; // How many rotation keyframes
346  }
347 
348  // argh! floats into two bytes!.. bad bad bad bad
349  // After fighting with it for a while.. -1, to 1 seems to give the best results
350  rotations = readKeys(data, ref i, rotationkeys, -1f, 1f);
351  for (int iter = 0; iter < rotations.Length; iter++)
352  {
353  rotations[iter].W = 1f -
354  (rotations[iter].key_element.X + rotations[iter].key_element.Y +
355  rotations[iter].key_element.Z);
356  }
357 
358 
359  if (!BitConverter.IsLittleEndian)
360  {
361  positionkeys = Utils.BytesToInt(BinBVHUtil.EndianSwap(data, i, 4)); i += 4; // How many position keyframes
362  }
363  else
364  {
365  positionkeys = Utils.BytesToInt(data, i); i += 4; // How many position keyframes
366  }
367 
368  // Read in position keyframes
369  // argh! more floats into two bytes!.. *head desk*
370  // After fighting with it for a while.. -5, to 5 seems to give the best results
371  positions = readKeys(data, ref i, positionkeys, -5f, 5f);
372 
373  pJoint.rotationkeys = rotations;
374  pJoint.positionkeys = positions;
375 
376  return pJoint;
377  }
378 
389  private binBVHJointKey[] readKeys(byte[] data, ref int i, int keycount, float min, float max)
390  {
391  float x;
392  float y;
393  float z;
394 
395  /*
396  0.o, Float values in Two bytes.. this is just wrong >:(
397  17 255 <-- Time Code
398  17 255 <-- Time Code
399  255 255 <-- X
400  127 127 <-- X
401  255 255 <-- Y
402  127 127 <-- Y
403  213 213 <-- Z
404  142 142 <---Z
405 
406  */
407 
408  binBVHJointKey[] m_keys = new binBVHJointKey[keycount];
409  for (int j = 0; j < keycount; j++)
410  {
411  binBVHJointKey pJKey = new binBVHJointKey();
412  if (!BitConverter.IsLittleEndian)
413  {
414  pJKey.time = Utils.UInt16ToFloat(BinBVHUtil.EndianSwap(data, i, 2), 0, InPoint, OutPoint); i += 2;
415  x = Utils.UInt16ToFloat(BinBVHUtil.EndianSwap(data, i, 2), 0, min, max); i += 2;
416  y = Utils.UInt16ToFloat(BinBVHUtil.EndianSwap(data, i, 2), 0, min, max); i += 2;
417  z = Utils.UInt16ToFloat(BinBVHUtil.EndianSwap(data, i, 2), 0, min, max); i += 2;
418  }
419  else
420  {
421  pJKey.time = Utils.UInt16ToFloat(data, i, InPoint, OutPoint); i += 2;
422  x = Utils.UInt16ToFloat(data, i, min, max); i += 2;
423  y = Utils.UInt16ToFloat(data, i, min, max); i += 2;
424  z = Utils.UInt16ToFloat(data, i, min, max); i += 2;
425  }
426  pJKey.key_element = new Vector3(x, y, z);
427  m_keys[j] = pJKey;
428  }
429  return m_keys;
430  }
431 
432 
433 
434  }
438  public struct binBVHJoint
439  {
443  public string Name;
444 
448  public int Priority;
449 
454 
460 
461 
462 
463  public void WriteBytesToStream(BinaryWriter iostream, float InPoint, float OutPoint)
464  {
465  iostream.Write(BinBVHUtil.WriteNullTerminatedString(Name));
466  iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(Priority)));
467  iostream.Write(BinBVHUtil.ES(Utils.IntToBytes(rotationkeys.Length)));
468  for (int i=0;i<rotationkeys.Length;i++)
469  {
470  rotationkeys[i].WriteBytesToStream(iostream, InPoint, OutPoint, -1f, 1f);
471  }
472  iostream.Write(BinBVHUtil.ES(Utils.IntToBytes((positionkeys.Length))));
473  for (int i = 0; i < positionkeys.Length; i++)
474  {
475  positionkeys[i].WriteBytesToStream(iostream, InPoint, OutPoint, -256f, 256f);
476  }
477  }
478  }
479 
483  public struct binBVHJointKey
484  {
485  // Time in seconds for this keyframe.
486  public float time;
487 
491  public Vector3 key_element;
492 
493  public float W;
494 
495  public void WriteBytesToStream(BinaryWriter iostream, float InPoint, float OutPoint, float min, float max)
496  {
497  iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(BinBVHUtil.FloatToUInt16(time, InPoint, OutPoint))));
498  iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(BinBVHUtil.FloatToUInt16(key_element.X, min, max))));
499  iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(BinBVHUtil.FloatToUInt16(key_element.Y, min, max))));
500  iostream.Write(BinBVHUtil.ES(Utils.UInt16ToBytes(BinBVHUtil.FloatToUInt16(key_element.Z, min, max))));
501  }
502  }
503 
507  public enum HandPose : uint
508  {
509  Spread = 0,
510  Relaxed = 1,
511  Point_Both = 2,
512  Fist = 3,
513  Relaxed_Left = 4,
514  Point_Left = 5,
515  Fist_Left = 6,
516  Relaxed_Right = 7,
517  Point_Right = 8,
518  Fist_Right = 9,
519  Salute_Right = 10,
520  Typing = 11,
521  Peace_Right = 12
522  }
523  public static class BinBVHUtil
524  {
525  public const float ONE_OVER_U16_MAX = 1.0f / UInt16.MaxValue;
526 
527  public static UInt16 FloatToUInt16(float val, float lower, float upper)
528  {
529  UInt16 uival = 0;
530  //m_parentGroup.GetTimeDilation() * (float)ushort.MaxValue
531  //0-1
532 
533 // float difference = upper - lower;
534  // we're trying to get a zero lower and modify all values equally so we get a percentage position
535  if (lower > 0)
536  {
537  upper -= lower;
538  val = val - lower;
539 
540  // start with 500 upper and 200 lower.. subtract 200 from the upper and the value
541  }
542  else //if (lower < 0 && upper > 0)
543  {
544  // double negative, 0 minus negative 5 is 5.
545  upper += 0 - lower;
546  lower += 0 - lower;
547  val += 0 - lower;
548  }
549 
550  if (upper == 0)
551  val = 0;
552  else
553  {
554  val /= upper;
555  }
556 
557  uival = (UInt16)(val * UInt16.MaxValue);
558 
559  return uival;
560  }
561 
562 
569  public static byte[] ES(byte[] arr)
570  {
571  if (!BitConverter.IsLittleEndian)
572  Array.Reverse(arr);
573  return arr;
574  }
575  public static byte[] EndianSwap(byte[] arr, int offset, int len)
576  {
577  byte[] bendian = new byte[offset + len];
578  Buffer.BlockCopy(arr, offset, bendian, 0, len);
579  Array.Reverse(bendian);
580  return bendian;
581  }
582 
583  public static byte[] WriteNullTerminatedString(string str)
584  {
585  byte[] output = new byte[str.Length + 1];
586  Char[] chr = str.ToCharArray();
587  int i = 0;
588  for (i = 0; i < chr.Length; i++)
589  {
590  output[i] = Convert.ToByte(chr[i]);
591 
592  }
593 
594  output[i] = Convert.ToByte('\0');
595  return output;
596  }
597 
598  }
599 }
600 /*
601 switch (jointname)
602  {
603  case "mPelvis":
604  case "mTorso":
605  case "mNeck":
606  case "mHead":
607  case "mChest":
608  case "mHipLeft":
609  case "mHipRight":
610  case "mKneeLeft":
611  case "mKneeRight":
612  // XYZ->ZXY
613  t = x;
614  x = y;
615  y = t;
616  break;
617  case "mCollarLeft":
618  case "mCollarRight":
619  case "mElbowLeft":
620  case "mElbowRight":
621  // YZX ->ZXY
622  t = z;
623  z = x;
624  x = y;
625  y = t;
626  break;
627  case "mWristLeft":
628  case "mWristRight":
629  case "mShoulderLeft":
630  case "mShoulderRight":
631  // ZYX->ZXY
632  t = y;
633  y = z;
634  z = t;
635 
636  break;
637  case "mAnkleLeft":
638  case "mAnkleRight":
639  // XYZ ->ZXY
640  t = x;
641  x = z;
642  z = y;
643  y = t;
644  break;
645  }
646 */
int Priority
Joint Animation Override? Was the same as the Priority in testing..
binBVHJoint[] Joints
Contains an array of joints
Single OutPoint
The time in seconds to end the animation
binBVHJointKey[] positionkeys
Array of Position Keyframes in order from earliest to latest This seems to only be for the Pelvis...
void WriteBytesToStream(BinaryWriter iostream, float InPoint, float OutPoint)
Single InPoint
The time in seconds to start the animation
Vector3 key_element
Either a Vector3 position or a Vector3 Euler rotation
HandPose
Poses set in the animation metadata for the hands.
Written to decode and encode a binary animation asset. The SecondLife Client reads in a BVH file and ...
A Joint and it's associated meta data and keyframes
string ExpressionName
Expression set in the client. Null if [None] is selected
OpenSim.Framework.Animation Animation
void WriteBytesToStream(BinaryWriter iostream, float InPoint, float OutPoint, float min, float max)
string Name
Name of the Joint. Matches the avatar_skeleton.xml in client distros
A Joint Keyframe. This is either a position or a rotation.
binBVHJointKey[] rotationkeys
Array of Rotation Keyframes in order from earliest to latest
Single Length
The animation length in seconds.