aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes/Animation/BinBVHAnimation.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/Animation/BinBVHAnimation.cs')
-rw-r--r--OpenSim/Region/Framework/Scenes/Animation/BinBVHAnimation.cs646
1 files changed, 646 insertions, 0 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Animation/BinBVHAnimation.cs b/OpenSim/Region/Framework/Scenes/Animation/BinBVHAnimation.cs
new file mode 100644
index 0000000..b3b38b2
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/Animation/BinBVHAnimation.cs
@@ -0,0 +1,646 @@
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
28using System;
29using System.IO;
30using OpenMetaverse;
31
32namespace OpenSim.Region.Framework.Scenes.Animation
33{
34 /// <summary>
35 /// Written to decode and encode a binary animation asset.
36 /// The SecondLife Client reads in a BVH file and converts
37 /// it to the format described here. This isn't
38 /// </summary>
39 public class BinBVHAnimation
40 {
41 /// <summary>
42 /// Rotation Keyframe count (used internally)
43 /// Don't use this, use the rotationkeys.Length on each joint
44 /// </summary>
45 private int rotationkeys;
46
47 /// <summary>
48 /// Position Keyframe count (used internally)
49 /// Don't use this, use the positionkeys.Length on each joint
50 /// </summary>
51 private int positionkeys;
52
53 public UInt16 unknown0; // Always 1
54 public UInt16 unknown1; // Always 0
55
56 /// <summary>
57 /// Animation Priority
58 /// </summary>
59 public int Priority;
60
61 /// <summary>
62 /// The animation length in seconds.
63 /// </summary>
64 public Single Length;
65
66 /// <summary>
67 /// Expression set in the client. Null if [None] is selected
68 /// </summary>
69 public string ExpressionName; // "" (null)
70
71 /// <summary>
72 /// The time in seconds to start the animation
73 /// </summary>
74 public Single InPoint;
75
76 /// <summary>
77 /// The time in seconds to end the animation
78 /// </summary>
79 public Single OutPoint;
80
81 /// <summary>
82 /// Loop the animation
83 /// </summary>
84 public bool Loop;
85
86 /// <summary>
87 /// Meta data. Ease in Seconds.
88 /// </summary>
89 public Single EaseInTime;
90
91 /// <summary>
92 /// Meta data. Ease out seconds.
93 /// </summary>
94 public Single EaseOutTime;
95
96 /// <summary>
97 /// Meta Data for the Hand Pose
98 /// </summary>
99 public uint HandPose;
100
101 /// <summary>
102 /// Number of joints defined in the animation
103 /// Don't use this.. use joints.Length
104 /// </summary>
105 private uint m_jointCount;
106
107
108 /// <summary>
109 /// Contains an array of joints
110 /// </summary>
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
147 public BinBVHAnimation()
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
239 /// <summary>
240 /// Variable length strings seem to be null terminated in the animation asset.. but..
241 /// use with caution, home grown.
242 /// advances the index.
243 /// </summary>
244 /// <param name="data">The animation asset byte array</param>
245 /// <param name="i">The offset to start reading</param>
246 /// <returns>a string</returns>
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
287 /// <summary>
288 /// Read in a Joint from an animation asset byte array
289 /// Variable length Joint fields, yay!
290 /// Advances the index
291 /// </summary>
292 /// <param name="data">animation asset byte array</param>
293 /// <param name="i">Byte Offset of the start of the joint</param>
294 /// <returns>The Joint data serialized into the binBVHJoint structure</returns>
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
379 /// <summary>
380 /// Read Keyframes of a certain type
381 /// advance i
382 /// </summary>
383 /// <param name="data">Animation Byte array</param>
384 /// <param name="i">Offset in the Byte Array. Will be advanced</param>
385 /// <param name="keycount">Number of Keyframes</param>
386 /// <param name="min">Scaling Min to pass to the Uint16ToFloat method</param>
387 /// <param name="max">Scaling Max to pass to the Uint16ToFloat method</param>
388 /// <returns></returns>
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 }
435 /// <summary>
436 /// A Joint and it's associated meta data and keyframes
437 /// </summary>
438 public struct binBVHJoint
439 {
440 /// <summary>
441 /// Name of the Joint. Matches the avatar_skeleton.xml in client distros
442 /// </summary>
443 public string Name;
444
445 /// <summary>
446 /// Joint Animation Override? Was the same as the Priority in testing..
447 /// </summary>
448 public int Priority;
449
450 /// <summary>
451 /// Array of Rotation Keyframes in order from earliest to latest
452 /// </summary>
453 public binBVHJointKey[] rotationkeys;
454
455 /// <summary>
456 /// Array of Position Keyframes in order from earliest to latest
457 /// This seems to only be for the Pelvis?
458 /// </summary>
459 public binBVHJointKey[] positionkeys;
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
480 /// <summary>
481 /// A Joint Keyframe. This is either a position or a rotation.
482 /// </summary>
483 public struct binBVHJointKey
484 {
485 // Time in seconds for this keyframe.
486 public float time;
487
488 /// <summary>
489 /// Either a Vector3 position or a Vector3 Euler rotation
490 /// </summary>
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
504 /// <summary>
505 /// Poses set in the animation metadata for the hands.
506 /// </summary>
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
563 /// <summary>
564 /// Endian Swap
565 /// Swaps endianness if necessary
566 /// </summary>
567 /// <param name="arr">Input array</param>
568 /// <returns></returns>
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/*
601switch (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*/ \ No newline at end of file