Jorrit Rouwe преди 4 години
ревизия
686ab6ab85
променени са 100 файла, в които са добавени 25492 реда и са изтрити 0 реда
  1. 17 0
      .gitattributes
  2. 6 0
      .gitignore
  3. 1654 0
      Assets/Human.tof
  4. 131 0
      Assets/Human/dead_pose1.tof
  5. 131 0
      Assets/Human/dead_pose2.tof
  6. 131 0
      Assets/Human/dead_pose3.tof
  7. 131 0
      Assets/Human/dead_pose4.tof
  8. 131 0
      Assets/Human/neutral.tof
  9. 3443 0
      Assets/Human/sprint.tof
  10. 5927 0
      Assets/Human/walk.tof
  11. 8 0
      Assets/LICENSE
  12. BIN
      Assets/convex_hulls.bin
  13. BIN
      Assets/heightfield1.bin
  14. BIN
      Assets/terrain1.bof
  15. BIN
      Assets/terrain2.bof
  16. BIN
      Assets/ui.tga
  17. 4 0
      Build/.gitignore
  18. 5 0
      Build/Android/.gitignore
  19. 48 0
      Build/Android/UnitTests/build.gradle
  20. 21 0
      Build/Android/UnitTests/src/main/AndroidManifest.xml
  21. 24 0
      Build/Android/UnitTests/src/main/cpp/CMakeLists.txt
  22. 17 0
      Build/Android/build.gradle
  23. 19 0
      Build/Android/gradle.properties
  24. BIN
      Build/Android/gradle/wrapper/gradle-wrapper.jar
  25. 5 0
      Build/Android/gradle/wrapper/gradle-wrapper.properties
  26. 185 0
      Build/Android/gradlew
  27. 89 0
      Build/Android/gradlew.bat
  28. 9 0
      Build/Android/settings.gradle
  29. 96 0
      Build/CMakeLists.txt
  30. 33 0
      Build/README.md
  31. 19 0
      Build/cmake_linux_clang.sh
  32. 3 0
      Build/cmake_vs2019_cl.bat
  33. 10 0
      Build/cmake_vs2019_clang.bat
  34. 9 0
      Build/unit_tests_coverage.bat
  35. 209 0
      Docs/Architecture.md
  36. BIN
      Docs/Logo.png
  37. BIN
      Docs/LogoSmall.png
  38. 0 0
      Docs/PhysicsSystemUpdate.gliffy
  39. 2 0
      Docs/PhysicsSystemUpdate.svg
  40. BIN
      Docs/SwingTwistConstraint.png
  41. 2571 0
      Doxyfile
  42. 224 0
      Jolt/AABBTree/AABBTreeBuilder.cpp
  43. 106 0
      Jolt/AABBTree/AABBTreeBuilder.h
  44. 316 0
      Jolt/AABBTree/AABBTreeToBuffer.h
  45. 290 0
      Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h
  46. 430 0
      Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h
  47. 67 0
      Jolt/Core/AlignedAllocator.h
  48. 32 0
      Jolt/Core/Atomics.h
  49. 73 0
      Jolt/Core/ByteBuffer.h
  50. 37 0
      Jolt/Core/Color.cpp
  51. 76 0
      Jolt/Core/Color.h
  52. 214 0
      Jolt/Core/Core.h
  53. 70 0
      Jolt/Core/FPControlWord.h
  54. 55 0
      Jolt/Core/FPException.h
  55. 31 0
      Jolt/Core/FPFlushDenormals.h
  56. 72 0
      Jolt/Core/Factory.cpp
  57. 45 0
      Jolt/Core/Factory.h
  58. 116 0
      Jolt/Core/FixedSizeFreeList.h
  59. 202 0
      Jolt/Core/FixedSizeFreeList.inl
  60. 51 0
      Jolt/Core/HashCombine.h
  61. 27 0
      Jolt/Core/IssueReporting.cpp
  62. 37 0
      Jolt/Core/IssueReporting.h
  63. 255 0
      Jolt/Core/JobSystem.h
  64. 55 0
      Jolt/Core/JobSystem.inl
  65. 570 0
      Jolt/Core/JobSystemThreadPool.cpp
  66. 150 0
      Jolt/Core/JobSystemThreadPool.h
  67. 50 0
      Jolt/Core/LinearCurve.cpp
  68. 65 0
      Jolt/Core/LinearCurve.h
  69. 121 0
      Jolt/Core/LockFreeHashMap.h
  70. 242 0
      Jolt/Core/LockFreeHashMap.inl
  71. 35 0
      Jolt/Core/Memory.cpp
  72. 14 0
      Jolt/Core/Memory.h
  73. 119 0
      Jolt/Core/Mutex.h
  74. 68 0
      Jolt/Core/MutexArray.h
  75. 17 0
      Jolt/Core/NonCopyable.h
  76. 384 0
      Jolt/Core/Profiler.cpp
  77. 232 0
      Jolt/Core/Profiler.h
  78. 87 0
      Jolt/Core/Profiler.inl
  79. 155 0
      Jolt/Core/RTTI.cpp
  80. 467 0
      Jolt/Core/RTTI.h
  81. 212 0
      Jolt/Core/Reference.h
  82. 175 0
      Jolt/Core/Result.h
  83. 369 0
      Jolt/Core/StatCollector.cpp
  84. 124 0
      Jolt/Core/StatCollector.h
  85. 304 0
      Jolt/Core/StaticArray.h
  86. 62 0
      Jolt/Core/StreamIn.h
  87. 49 0
      Jolt/Core/StreamOut.h
  88. 49 0
      Jolt/Core/StreamWrapper.h
  89. 96 0
      Jolt/Core/StringTools.cpp
  90. 48 0
      Jolt/Core/StringTools.h
  91. 100 0
      Jolt/Core/TempAllocator.h
  92. 86 0
      Jolt/Core/TickCounter.cpp
  93. 31 0
      Jolt/Core/TickCounter.h
  94. 279 0
      Jolt/Geometry/AABox.h
  95. 191 0
      Jolt/Geometry/AABox4.h
  96. 196 0
      Jolt/Geometry/ClipPoly.h
  97. 423 0
      Jolt/Geometry/ClosestPoint.h
  98. 1375 0
      Jolt/Geometry/ConvexHullBuilder.cpp
  99. 243 0
      Jolt/Geometry/ConvexHullBuilder.h
  100. 335 0
      Jolt/Geometry/ConvexHullBuilder2D.cpp

+ 17 - 0
.gitattributes

@@ -0,0 +1,17 @@
+# Convert LF to CRLF on windows on checkout and convert back before submitting to the repository
+* text=auto
+
+# Explicitly declare text files to always be normalized and converted to native line endings on checkout
+*.cpp text
+*.inl text
+*.h text
+*.tof text
+*.bat text
+
+# Force shell files to use LF only
+*.sh text eol=lf
+
+# Declare binary file types
+*.tga binary
+*.bof binary
+*.bin binary

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+.vs
+/profile_list_*.html
+/profile_chart_*.html
+/stats*.html
+/Docs/JoltPhysics.chm
+/Docs/JoltPhysics.chw

+ 1654 - 0
Assets/Human.tof

@@ -0,0 +1,1654 @@
+TOS 1.00
+
+declare RagdollSettings 2
+  mSkeleton instance Skeleton 
+  mParts array instance RagdollSettings::Part 
+
+declare Skeleton 1
+  mJoints array instance Skeleton::Joint 
+
+declare RagdollSettings::Part 19
+  mPosition vec3
+  mRotation quat
+  mShape instance ShapeSettings 
+  mCollisionGroup instance CollisionGroup 
+  mObjectLayer uint32
+  mMotionType uint32
+  mAllowDynamicOrKinematic bool
+  mMotionQuality uint32
+  mAllowSleeping bool
+  mFriction float
+  mRestitution float
+  mLinearDamping float
+  mAngularDamping float
+  mMaxLinearVelocity float
+  mMaxAngularVelocity float
+  mGravityFactor float
+  mOverrideMassProperties uint32
+  mMassPropertiesOverride instance MassProperties 
+  mToParent instance TwoBodyConstraintSettings 
+
+declare Skeleton::Joint 2
+  mName string
+  mParentName string
+
+declare ShapeSettings 1
+  mUserData uint32
+
+declare CollisionGroup 3
+  mGroupFilter instance GroupFilter 
+  mGroupID uint32
+  mSubGroupID uint32
+
+declare MassProperties 2
+  mMass float
+  mInertia mat44
+
+declare TwoBodyConstraintSettings 1
+  mDrawConstraintSize float
+
+declare GroupFilter 0
+
+object RagdollSettings 00000001
+  00000002
+  23
+      0 0.931122005 -0.0230349991
+      0 1 0 0
+      00000003
+        00000004
+        0
+        0
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000000
+      0.104854003 0.930898011 -0.0231149998
+      0.000290353579 -0.0122133596 0.999642909 -0.0237674899
+      00000005
+        00000004
+        0
+        1
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000006
+      0.126794994 0.469749004 -0.0343980007
+      -0.00113988295 0.0479386896 0.998567462 -0.0237417668
+      00000007
+        00000004
+        0
+        2
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000008
+      0.145157993 0.0837970003 0.00278700003
+      0 -0.506379366 0.862310827 0
+      00000009
+        00000004
+        0
+        3
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000000A
+      -0.104854003 0.930896997 -0.0231149998
+      0.0237674899 0.999642909 0.0122133596 0.000290353579
+      0000000B
+        00000004
+        0
+        4
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000000C
+      -0.126794994 0.469749004 -0.0343980007
+      0.0237417668 0.998567462 -0.0479386896 -0.00113988295
+      0000000D
+        00000004
+        0
+        5
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000000E
+      -0.145156994 0.0837979987 0.00278700003
+      0 0.862310827 0.506379366 0
+      0000000F
+        00000004
+        0
+        6
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000010
+      0 0.986563981 -0.0230349991
+      0 1 0 0
+      00000011
+        00000004
+        0
+        7
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000012
+      0 1.03556395 -0.0230349991
+      0 1 0 0
+      00000013
+        00000004
+        0
+        8
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000014
+      0 1.08456397 -0.0230349991
+      0 1 0 0
+      00000015
+        00000004
+        0
+        9
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000016
+      0 1.18256497 -0.0230349991
+      0 1 0 0
+      00000017
+        00000004
+        0
+        10
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000018
+      -0.00729899993 1.39142203 -0.0537119992
+      -0.144681364 0.131127819 -0.761695445 0.617812991
+      00000019
+        00000004
+        0
+        11
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000001A
+      -0.154914007 1.35279202 0.0128229996
+      0.0160888918 -0.00676369248 -0.387456775 0.921722651
+      0000001B
+        00000004
+        0
+        12
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000001C
+      -0.341015011 1.17054999 0.00372699997
+      0.186154276 -0.0782527998 -0.379532725 0.902872145
+      0000001D
+        00000004
+        0
+        13
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000001E
+      -0.499992013 1.01485896 -0.0921010002
+      0.186154276 -0.0782527998 -0.379532725 0.902872145
+      0000001F
+        00000004
+        0
+        14
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000020
+      0.00729899993 1.39142203 -0.0537119992
+      0.617812574 0.761695743 0.131128147 0.144681096
+      00000021
+        00000004
+        0
+        15
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000022
+      0.154914007 1.35279 0.0128229996
+      0.921722353 0.38745746 -0.00676369481 -0.0160888974
+      00000023
+        00000004
+        0
+        16
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000024
+      0.341015011 1.17054605 0.00372699997
+      0.902871907 0.379533201 -0.0782528147 -0.18615438
+      00000025
+        00000004
+        0
+        17
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000026
+      0.499992996 1.01486397 -0.0921010002
+      0.902871907 0.379533201 -0.0782528147 -0.18615438
+      00000027
+        00000004
+        0
+        18
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000028
+      0 1.38891399 0.0150830001
+      0 0.976295888 -0.216440096 0
+      00000029
+        00000004
+        0
+        19
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000002A
+      0 1.43785501 -0.00773900002
+      0 1 0 0
+      0000002B
+        00000004
+        0
+        20
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000002C
+      0 1.49185598 -0.00773900002
+      0 1 0 0
+      0000002D
+        00000004
+        0
+        21
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      0000002E
+      0 1.545856 -0.00773900002
+      0 1 0 0
+      0000002F
+        00000004
+        0
+        22
+      4
+      2
+      false
+      0
+      true
+      1
+      0.150000006
+      0.0500000007
+      0.0500000007
+      500
+      47.1238899
+      1
+      0
+        0
+        0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+      00000030
+
+object Skeleton 00000002
+  23
+      "hipsBone"
+      ""
+      "R_Leg_sjnt_0"
+      "hipsBone"
+      "R_Leg_sjnt_1"
+      "R_Leg_sjnt_0"
+      "R_Foot_sjnt_0"
+      "R_Leg_sjnt_1"
+      "L_Leg_sjnt_0"
+      "hipsBone"
+      "L_Leg_sjnt_1"
+      "L_Leg_sjnt_0"
+      "L_Foot_sjnt_0"
+      "L_Leg_sjnt_1"
+      "C_Spine_sjnt_0"
+      "hipsBone"
+      "C_Spine_sjnt_1"
+      "C_Spine_sjnt_0"
+      "C_Spine_sjnt_2"
+      "C_Spine_sjnt_1"
+      "C_Spine_sjnt_4"
+      "C_Spine_sjnt_2"
+      "L_Clavicle_sjnt_0"
+      "C_Spine_sjnt_4"
+      "L_Arm_sjnt_0"
+      "L_Clavicle_sjnt_0"
+      "L_Arm_sjnt_1"
+      "L_Arm_sjnt_0"
+      "L_Wrist_sjnt_0"
+      "L_Arm_sjnt_1"
+      "R_Clavicle_sjnt_0"
+      "C_Spine_sjnt_4"
+      "R_Arm_sjnt_0"
+      "R_Clavicle_sjnt_0"
+      "R_Arm_sjnt_1"
+      "R_Arm_sjnt_0"
+      "R_Wrist_sjnt_0"
+      "R_Arm_sjnt_1"
+      "C_Neck_sjnt_0"
+      "C_Spine_sjnt_4"
+      "C_Neck_sjnt_1"
+      "C_Neck_sjnt_0"
+      "C_Neck_sjnt_2"
+      "C_Neck_sjnt_1"
+      "C_Head_sjnt_0"
+      "C_Neck_sjnt_2"
+
+declare StaticCompoundShapeSettings 2
+  mUserData uint32
+  mSubShapes array instance CompoundShapeSettings::SubShapeSettings 
+
+declare CompoundShapeSettings::SubShapeSettings 4
+  mShape instance ShapeSettings 
+  mPosition vec3
+  mRotation quat
+  mUserData uint32
+
+object StaticCompoundShapeSettings 00000003
+  0
+  1
+      00000031
+      9.89999971e-05 -0.0391060002 1.80000006e-05
+      0.503439724 0.499906629 -0.498577178 0.498058915
+      0
+
+declare GroupFilterTable 2
+  mNumSubGroups uint32
+  mTable array uint8
+
+object GroupFilterTable 00000004
+  23
+  32
+    154
+    191
+    207
+    255
+    247
+    239
+    191
+    255
+    253
+    223
+    255
+    251
+    255
+    254
+    231
+    255
+    127
+    255
+    255
+    254
+    255
+    251
+    159
+    251
+    255
+    220
+    253
+    255
+    191
+    255
+    255
+    231
+
+object StaticCompoundShapeSettings 00000005
+  0
+  1
+      00000032
+      0.00401599985 0.249686003 -0.0112770004
+      0.99988085 0.00937711447 0.000316537742 -0.0122607099
+      0
+
+declare SwingTwistConstraintSettings 14
+  mDrawConstraintSize float
+  mPosition1 vec3
+  mTwistAxis1 vec3
+  mPlaneAxis1 vec3
+  mPosition2 vec3
+  mTwistAxis2 vec3
+  mPlaneAxis2 vec3
+  mNormalHalfConeAngle float
+  mPlaneHalfConeAngle float
+  mTwistMinAngle float
+  mTwistMaxAngle float
+  mMaxFrictionTorque float
+  mSwingMotorSettings instance MotorSettings 
+  mTwistMotorSettings instance MotorSettings 
+
+declare MotorSettings 6
+  mFrequency float
+  mDamping float
+  mMinForceLimit float
+  mMaxForceLimit float
+  mMinTorqueLimit float
+  mMaxTorqueLimit float
+
+object SwingTwistConstraintSettings 00000006
+  0.100000001
+  0.104854003 0.930898011 -0.0231149998
+  0.228872508 -0.936023414 -0.267353028
+  -0.97338593 -0.216752678 -0.0744198412
+  0.104854003 0.930898011 -0.0231149998
+  0.0758192614 -0.996863306 -0.0227017533
+  -0.995388806 -0.0743260905 -0.0606405661
+  0.785398006
+  0.595446467
+  -0.578072011
+  0.578072011
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 00000007
+  0
+  1
+      00000033
+      -0.000671999995 0.193944007 0.00204199995
+      0.999895334 -0.000285029731 0.000180768882 0.0144680059
+      0
+
+object SwingTwistConstraintSettings 00000008
+  0.100000001
+  0.126794919 0.469749451 -0.0343977995
+  0.0052347132 -0.518037319 0.85534209
+  -0.999879241 -0.0152359791 -0.00310837477
+  0.126794994 0.469749004 -0.0343980007
+  0.0144062117 -0.995301723 0.095745571
+  -0.99988842 -0.0147217829 -0.002590355
+  1.04719996
+  0.27414149
+  -0.174532995
+  0.174532995
+  2.5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+
+object StaticCompoundShapeSettings 00000009
+  0
+  1
+      00000034
+      -0.000341000006 0.0795179978 0.00706599979
+      0.515692592 -3.67657522e-05 -0.000328265945 0.856773615
+      0
+
+object SwingTwistConstraintSettings 0000000A
+  0.100000001
+  0.145157948 0.0837986767 0.00278661028
+  0.0377257243 -0.542519033 -0.839195669
+  -0.998523057 -0.0533199608 -0.0104182065
+  0.145157993 0.0837970003 0.00278700003
+  0.0118515138 -0.486690015 -0.873494208
+  -0.9991166 -0.0409819633 0.00927796029
+  1.03831005
+  0.498153508
+  -0.350461006
+  0.350461006
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 0000000B
+  0
+  1
+      00000035
+      -0.00433799997 -0.249614999 0.0112300003
+      0.0122669889 -9.97621828e-05 0.00963442121 0.999878347
+      0
+
+object SwingTwistConstraintSettings 0000000C
+  0.100000001
+  -0.104854003 0.930896997 -0.0231149998
+  -0.276837289 -0.923905373 -0.264120668
+  -0.960916519 0.266360998 0.0754395574
+  -0.104854003 0.930896997 -0.0231149998
+  -0.126640797 -0.991754949 -0.0196007676
+  -0.990183711 0.125212267 0.0621150807
+  0.785398006
+  0.593013525
+  -0.578072011
+  0.578072011
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 0000000D
+  0
+  1
+      00000036
+      0.000577999977 -0.193875998 -0.00208500004
+      -0.014465007 3.40035476e-05 -2.30024016e-05 0.999895394
+      0
+
+object SwingTwistConstraintSettings 0000000E
+  0.100000001
+  -0.126794875 0.46974951 -0.0343977734
+  -0.00985482056 -0.517950952 0.855353653
+  -0.999714077 0.0237438455 0.00285971398
+  -0.126794994 0.469749004 -0.0343980007
+  -0.0229026247 -0.995140314 0.0957655832
+  -0.999727368 0.0232333038 0.00233921455
+  1.04719996
+  0.243131503
+  -0.174532995
+  0.174532995
+  2.5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+
+object StaticCompoundShapeSettings 0000000F
+  0
+  1
+      00000037
+      0.00325600011 -0.0925799981 -0.0138490004
+      0.871728599 0 0 -0.489989072
+      0
+
+object SwingTwistConstraintSettings 00000010
+  0.100000001
+  -0.14515689 0.08379969 0.00278650969
+  -0.0107628927 -0.543768108 -0.83916676
+  -0.999097705 0.0403309204 -0.0133193471
+  -0.145156994 0.0837979987 0.00278700003
+  0.0151939169 -0.487636805 -0.872914493
+  -0.999063849 0.0279592033 -0.0330082364
+  1.03831005
+  0.476549506
+  -0.313739002
+  0.313739002
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 00000011
+  0
+  1
+      00000038
+      -0 0.0190730002 0.00825699978
+      0.5 0.5 -0.5 0.5
+      0
+
+object SwingTwistConstraintSettings 00000012
+  0.100000001
+  0 0.986563981 -0.0230349991
+  0.00122399244 0.999999285 1.79180237e-09
+  -0.999999285 0.00122399244 -2.92779873e-06
+  0 0.986563981 -0.0230349991
+  0.00122399593 0.999996424 -0.00239199842
+  -0.999999285 0.00122399244 -2.92779873e-06
+  0.516243994
+  0.418725014
+  -0.443857014
+  0.443857014
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 00000013
+  0
+  1
+      00000039
+      -0 0.0677639991 0.0116400002
+      0.5 0.5 -0.5 0.5
+      0
+
+object SwingTwistConstraintSettings 00000014
+  0.100000001
+  0 1.03556395 -0.0230349991
+  -0.0170466602 0.999854624 -0.000102737526
+  -0.999854624 -0.0170466602 0.000242655806
+  0 1.03556395 -0.0230349991
+  -0.0170036666 0.99985534 -9.5737596e-05
+  -0.99985534 -0.0170036666 0.000192655934
+  0.629583001
+  0.448353499
+  -0.373118013
+  0.373118013
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 00000015
+  0
+  1
+      0000003A
+      -0 0.124006003 0.0164899994
+      0.5 0.5 -0.5 0.5
+      0
+
+object SwingTwistConstraintSettings 00000016
+  0.100000001
+  0 1.08456397 -0.0230349991
+  0.005554453 0.999984562 3.68993263e-08
+  -0.999984562 0.005554453 -1.32862906e-05
+  0 1.08456397 -0.0230349991
+  0.00555446884 0.999981701 -0.00239196327
+  -0.999984562 0.005554453 -1.32862906e-05
+  0.583182991
+  0.422738492
+  -0.423810005
+  0.423810005
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 00000017
+  0
+  1
+      0000003B
+      0 0.100479998 -0.00150599994
+      0.5 0.5 -0.5 0.5
+      0
+
+object SwingTwistConstraintSettings 00000018
+  0.100000001
+  0 1.18256402 -0.0230349991
+  -0.00044950709 0.999999702 2.41660497e-10
+  -0.999999702 -0.00044950709 1.07522419e-06
+  0 1.18256497 -0.0230349991
+  -0.00044950837 0.999996841 -0.00239199959
+  -0.999999702 -0.00044950709 1.07522419e-06
+  0.466737002
+  0.361206502
+  -0.426357001
+  0.426357001
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 00000019
+  0
+  1
+      0000003C
+      0.02348 -0.103221998 0.0195889995
+      -0.0207751915 0.785383642 -0.618652523 -0.00316055887
+      0
+
+object SwingTwistConstraintSettings 0000001A
+  0.100000001
+  -0.00729800016 1.39142096 -0.0537109971
+  -0.898493886 -0.109122984 0.425206929
+  -0.414041013 -0.111203998 -0.903439999
+  -0.00729899993 1.39142203 -0.0537119992
+  -0.827898681 -0.1581029 0.538133204
+  -0.54195267 -0.0216289852 -0.840130627
+  0.785398006
+  0.610864997
+  -0.349065989
+  0.349065989
+  2.5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+
+object StaticCompoundShapeSettings 0000001B
+  0
+  1
+      0000003D
+      0.00292499992 -0.120290004 0.00187000004
+      0.999328971 0.0345016569 -0.000774269341 -0.0122714825
+      0
+
+object SwingTwistConstraintSettings 0000001C
+  0.100000001
+  -0.154913723 1.35279238 0.0128222816
+  -0.819559813 -0.545983851 0.173848867
+  -0.209418848 0.00299960375 -0.97782141
+  -0.154914007 1.35279202 0.0128229996
+  -0.712775946 -0.698031902 0.0685697496
+  -0.0496755503 -0.0472769961 -0.997645676
+  1.11110997
+  0.890465975
+  -1.37300003
+  1.37300003
+  2.5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+
+object StaticCompoundShapeSettings 0000001D
+  0
+  1
+      0000003E
+      0.005382 -0.131909996 0.00233300007
+      0.0106607033 -0.000188512466 0.00426478311 0.999934077
+      0
+
+object SwingTwistConstraintSettings 0000001E
+  0.100000001
+  -0.341012955 1.17055202 0.00372701138
+  -0.18491815 -0.181086376 -0.965925932
+  0.700336695 -0.71381259 -0.000252231723
+  -0.341015011 1.17054999 0.00372699997
+  -0.656364143 -0.642424464 -0.395571738
+  0.69514966 -0.718732536 0.0138015067
+  1.15708005
+  0.436331987
+  -0.392699003
+  0.392699003
+  2.5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+
+object StaticCompoundShapeSettings 0000001F
+  0
+  1
+      0000003F
+      0.0277379993 -0.0670339987 0
+      0 0 0.117515303 0.993071079
+      0
+
+object SwingTwistConstraintSettings 00000020
+  0.100000001
+  -0.499991 1.01485944 -0.0920999721
+  -0.656863928 -0.64203316 -0.395376891
+  0.446287364 0.0916005597 -0.890189111
+  -0.499992013 1.01485896 -0.0921010002
+  -0.656693518 -0.642100096 -0.395551473
+  0.453417242 0.0829586834 -0.887429178
+  1.09455001
+  0.659080029
+  -0.920177996
+  0.920177996
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 00000021
+  0
+  1
+      00000040
+      -0.0250979997 0.100487001 -0.0217920002
+      -0.0207751915 0.785383642 -0.618652523 -0.00316055887
+      0
+
+object SwingTwistConstraintSettings 00000022
+  0.100000001
+  0.00729800016 1.39142096 -0.0537109971
+  0.887680888 -0.181781992 0.423057944
+  -0.414462924 0.0848429799 0.906102777
+  0.00729899993 1.39142203 -0.0537119992
+  0.82955873 -0.159525871 0.535148382
+  -0.536151767 0.0403930396 0.84315455
+  0.785398006
+  0.610864997
+  -0.349065989
+  0.349065989
+  2.5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+
+object StaticCompoundShapeSettings 00000023
+  0
+  1
+      00000041
+      -0.00255899993 0.120980002 -0.001666
+      0.0125852004 0.00044055449 0.0349786803 0.999308705
+      0
+
+object SwingTwistConstraintSettings 00000024
+  0.100000001
+  0.154913425 1.3527894 0.0128220581
+  0.917809129 -0.357491851 -0.172701865
+  0.107674032 -0.194559038 0.97496295
+  0.154914007 1.35279 0.0128229996
+  0.707821548 -0.692629874 -0.13875258
+  0.0966861993 -0.0995812863 0.990320802
+  1.11110997
+  0.890465975
+  -1.37300003
+  1.37300003
+  2.5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+
+object StaticCompoundShapeSettings 00000025
+  0
+  1
+      00000042
+      -0.00529700005 0.132655993 -0.00221200008
+      0.999927938 0.00482584862 0.000318023056 -0.0109857954
+      0
+
+object SwingTwistConstraintSettings 00000026
+  0.100000001
+  0.341013938 1.1705482 0.00372693874
+  0.118063569 -0.186914414 -0.975255847
+  0.705077827 0.707349956 -0.0502125323
+  0.341015011 1.17054605 0.00372699997
+  0.656227231 -0.64257741 -0.395550787
+  0.668211818 0.738391399 -0.0909467712
+  1.14461005
+  0.436331987
+  -0.392699003
+  0.392699003
+  2.5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -500
+    500
+
+object StaticCompoundShapeSettings 00000027
+  0
+  1
+      00000043
+      -0.0236859992 0.0719750002 -0.00162500003
+      0.993005037 0.118071899 0.000114551302 -0.00034189166
+      0
+
+object SwingTwistConstraintSettings 00000028
+  0.100000001
+  0.499991298 1.0148654 -0.0921003968
+  0.65732044 -0.642059684 -0.394574851
+  0.445664227 -0.0910333097 0.890559733
+  0.499992996 1.01486397 -0.0921010002
+  0.657148719 -0.64212811 -0.394749463
+  0.45279476 -0.0823907033 0.887799919
+  1.09455001
+  0.659080029
+  -0.920177996
+  0.920177996
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 00000029
+  0
+  1
+      00000044
+      0 0.0190360006 0.00591100007
+      -0.823387563 0 0 0.567479491
+      0
+
+object SwingTwistConstraintSettings 0000002A
+  0.100000001
+  0 1.38891292 0.0150830019
+  -0.0614751019 0.93085885 -0.360169828
+  -0.998099923 -0.0558407158 0.0260390211
+  0 1.38891399 0.0150830001
+  -0.0616134368 0.904585302 -0.42181617
+  -0.998099923 -0.0558407158 0.0260390211
+  0.602330983
+  0.47467351
+  -0.5
+  0.5
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 0000002B
+  0
+  2
+      00000045
+      -0.0285969991 0.0289249998 -0.00178199995
+      0.76383245 0 0 0.645414591
+      0
+      00000046
+      0.0288059991 0.028926 -0.00178199995
+      0.76383245 0 0 0.645414591
+      0
+
+object SwingTwistConstraintSettings 0000002C
+  0.100000001
+  0 1.43785453 -0.00773843564
+  -0.0748678446 0.997159719 0.00818838924
+  -0.997193158 -0.0748703554 0
+  0 1.43785501 -0.00773900002
+  -0.0748703554 0.997193158 0
+  -0.997193158 -0.0748703554 0
+  0.524141014
+  0.421988487
+  -0.501203001
+  0.501203001
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 0000002D
+  0
+  2
+      00000047
+      -0.0221040007 0.0260649994 0.0262419991
+      -0.600778699 0 0 0.79941541
+      0
+      00000048
+      0.0236319993 0.0260649994 0.0262419991
+      -0.600778699 0 0 0.79941541
+      0
+
+object SwingTwistConstraintSettings 0000002E
+  0.100000001
+  0 1.49185503 -0.00773900002
+  0.0192970913 0.999753535 -0.0109725529
+  -0.999813676 0.0192977451 -4.61603486e-05
+  0 1.49185598 -0.00773900002
+  0.019297801 0.999810815 -0.00239155442
+  -0.999813676 0.0192977451 -4.61603486e-05
+  0.601015985
+  0.499635011
+  -0.408565998
+  0.408565998
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+object StaticCompoundShapeSettings 0000002F
+  0
+  1
+      00000049
+      -0 0.0480049998 0.0185359996
+      0.89858973 0 0 0.438789845
+      0
+
+object SwingTwistConstraintSettings 00000030
+  0.100000001
+  0 1.545856 -0.00773900002
+  -0.0299134608 0.958364487 -0.283977121
+  -0.999513268 -0.031197831 0
+  0 1.545856 -0.00773900002
+  -0.040179193 0.999192536 0
+  -0.999192536 -0.040179193 0
+  0.793327987
+  0.66770196
+  -0.5
+  0.5
+  5
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+    20
+    2
+    -3.40282347e+38
+    3.40282347e+38
+    -1000
+    1000
+
+declare CapsuleShapeSettings 5
+  mUserData uint32
+  mDensity float
+  mMaterial instance PhysicsMaterial 
+  mRadius float
+  mHalfHeightOfCylinder float
+
+declare PhysicsMaterial 0
+
+object CapsuleShapeSettings 00000031
+  0
+  1000
+  00000000
+  0.105885997
+  0.0730924979
+
+declare TaperedCapsuleShapeSettings 6
+  mUserData uint32
+  mDensity float
+  mMaterial instance PhysicsMaterial 
+  mHalfHeightOfTaperedCylinder float
+  mTopRadius float
+  mBottomRadius float
+
+object TaperedCapsuleShapeSettings 00000032
+  0
+  1000
+  00000000
+  0.146803498
+  0.0799999982
+  0.0599999987
+
+object TaperedCapsuleShapeSettings 00000033
+  0
+  1000
+  00000000
+  0.161571503
+  0.0599999987
+  0.0399999991
+
+declare BoxShapeSettings 5
+  mUserData uint32
+  mDensity float
+  mMaterial instance PhysicsMaterial 
+  mHalfExtent vec3
+  mConvexRadius float
+
+object BoxShapeSettings 00000034
+  0
+  1000
+  00000000
+  0.0444360003 0.0361290015 0.112469502
+  0.00361290015
+
+object TaperedCapsuleShapeSettings 00000035
+  0
+  1000
+  00000000
+  0.146895498
+  0.0799999982
+  0.0599999987
+
+object TaperedCapsuleShapeSettings 00000036
+  0
+  1000
+  00000000
+  0.161571503
+  0.0599999987
+  0.0399999991
+
+object BoxShapeSettings 00000037
+  0
+  1000
+  00000000
+  0.0444360003 0.0361290015 0.112469502
+  0.00361290015
+
+object CapsuleShapeSettings 00000038
+  0
+  1000
+  00000000
+  0.0688254014
+  0.0751734972
+
+object CapsuleShapeSettings 00000039
+  0
+  1000
+  00000000
+  0.0601567999
+  0.0696785003
+
+object CapsuleShapeSettings 0000003A
+  0
+  1000
+  00000000
+  0.0976348966
+  0.0449381992
+
+object CapsuleShapeSettings 0000003B
+  0
+  1000
+  00000000
+  0.0970778018
+  0.0229662005
+
+object CapsuleShapeSettings 0000003C
+  0
+  1000
+  00000000
+  0.0527785011
+  0.0154518504
+
+object TaperedCapsuleShapeSettings 0000003D
+  0
+  1000
+  00000000
+  0.117732003
+  0.0455139019
+  0.0500000007
+
+object TaperedCapsuleShapeSettings 0000003E
+  0
+  1000
+  00000000
+  0.108483002
+  0.0399999991
+  0.0295928996
+
+object CapsuleShapeSettings 0000003F
+  0
+  1000
+  00000000
+  0.0302533992
+  0.0436993986
+
+object CapsuleShapeSettings 00000040
+  0
+  1000
+  00000000
+  0.0527775995
+  0.0154472999
+
+object TaperedCapsuleShapeSettings 00000041
+  0
+  1000
+  00000000
+  0.117731497
+  0.0455139019
+  0.0500000007
+
+object TaperedCapsuleShapeSettings 00000042
+  0
+  1000
+  00000000
+  0.108475499
+  0.0399999991
+  0.0295928996
+
+object CapsuleShapeSettings 00000043
+  0
+  1000
+  00000000
+  0.0302533992
+  0.0436993986
+
+object CapsuleShapeSettings 00000044
+  0
+  1000
+  00000000
+  0.0548548996
+  0.0267487504
+
+object CapsuleShapeSettings 00000045
+  0
+  1000
+  00000000
+  0.0304937996
+  0.0251529999
+
+object CapsuleShapeSettings 00000046
+  0
+  1000
+  00000000
+  0.0304937996
+  0.0251529999
+
+object CapsuleShapeSettings 00000047
+  0
+  1000
+  00000000
+  0.0268144999
+  0.0475768
+
+object CapsuleShapeSettings 00000048
+  0
+  1000
+  00000000
+  0.0268144999
+  0.0475768
+
+object CapsuleShapeSettings 00000049
+  0
+  1000
+  00000000
+  0.0832396001
+  0.0183336493

+ 131 - 0
Assets/Human/dead_pose1.tof

@@ -0,0 +1,131 @@
+TOS 1.00
+
+declare SkeletalAnimation 1
+  mAnimatedJoints array instance SkeletalAnimation::AnimatedJoint 
+
+declare SkeletalAnimation::AnimatedJoint 2
+  mJointName string
+  mKeyframes array instance SkeletalAnimation::Keyframe 
+
+declare SkeletalAnimation::Keyframe 3
+  mRotation quat
+  mTranslation vec3
+  mTime float
+
+object SkeletalAnimation 00000001
+  23
+      "hipsBone"
+      1
+          -0.580907 0.662465 0.472926 -0.005293
+          -0.511629 0.123338 0.102563
+          0.000000
+      "R_Leg_sjnt_0"
+      1
+          0.994683 0.029515 -0.098649 -0.001901
+          -0.104854 -0.000224 0.000080
+          0.000000
+      "R_Leg_sjnt_1"
+      1
+          0.558236 0.082819 0.102170 0.819191
+          -0.000000 0.461809 0.000000
+          0.000000
+      "R_Foot_sjnt_0"
+      1
+          -0.415795 -0.025903 -0.285547 0.863080
+          0.000000 0.388174 0.000000
+          0.000000
+      "L_Leg_sjnt_0"
+      1
+          -0.096847 0.200302 0.053881 0.973446
+          0.104854 -0.000225 0.000080
+          0.000000
+      "L_Leg_sjnt_1"
+      1
+          0.625317 0.040626 -0.068376 0.776307
+          0.000000 -0.461808 0.000000
+          0.000000
+      "L_Foot_sjnt_0"
+      1
+          0.448028 0.016552 0.068711 -0.891222
+          -0.000001 -0.388173 -0.000000
+          0.000000
+      "C_Spine_sjnt_0"
+      1
+          0.031433 -0.000518 -0.001091 0.999505
+          -0.000000 0.055442 -0.000000
+          0.000000
+      "C_Spine_sjnt_1"
+      1
+          0.062272 0.003369 -0.006740 0.998031
+          -0.000000 0.049000 0.000000
+          0.000000
+      "C_Spine_sjnt_2"
+      1
+          0.093196 0.002904 -0.011009 0.995583
+          0.000000 0.049000 -0.000000
+          0.000000
+      "C_Spine_sjnt_4"
+      1
+          0.092643 0.004648 -0.011669 0.995620
+          0.000000 0.098001 0.000000
+          0.000000
+      "L_Clavicle_sjnt_0"
+      1
+          0.856473 -0.498143 0.097625 0.093688
+          0.007299 0.208857 0.030677
+          0.000000
+      "L_Arm_sjnt_0"
+      1
+          0.051326 0.583453 0.540585 0.603918
+          0.070455 -0.150703 0.005819
+          0.000000
+      "L_Arm_sjnt_1"
+      1
+          0.749181 -0.055037 -0.190752 0.631912
+          -0.000001 -0.260631 0.000000
+          0.000000
+      "L_Wrist_sjnt_0"
+      1
+          0.000688 -0.106898 0.049180 -0.993053
+          0.000007 -0.242273 0.000003
+          0.000000
+      "R_Clavicle_sjnt_0"
+      1
+          -0.076883 -0.107965 0.509223 0.850367
+          -0.007299 0.208857 0.030677
+          0.000000
+      "R_Arm_sjnt_0"
+      1
+          0.622778 -0.313061 0.547977 0.462453
+          -0.070458 0.150703 -0.005820
+          0.000000
+      "R_Arm_sjnt_1"
+      1
+          0.570264 -0.066970 -0.307114 0.758944
+          -0.000001 0.260632 -0.000000
+          0.000000
+      "R_Wrist_sjnt_0"
+      1
+          -0.107633 -0.182094 -0.068157 -0.974993
+          0.000000 0.242268 -0.000000
+          0.000000
+      "C_Neck_sjnt_0"
+      1
+          0.148132 0.042497 -0.052729 0.986646
+          0.000000 0.206349 -0.038118
+          0.000000
+      "C_Neck_sjnt_1"
+      1
+          -0.272572 0.021316 -0.048793 0.960661
+          -0.000000 0.054000 0.000000
+          0.000000
+      "C_Neck_sjnt_2"
+      1
+          -0.054183 -0.008292 -0.029041 0.998074
+          0.000000 0.054001 0.000000
+          0.000000
+      "C_Head_sjnt_0"
+      1
+          0.029305 0.088480 -0.137100 0.986162
+          0.000000 0.054000 -0.000000
+          0.000000

+ 131 - 0
Assets/Human/dead_pose2.tof

@@ -0,0 +1,131 @@
+TOS 1.00
+
+declare SkeletalAnimation 1
+  mAnimatedJoints array instance SkeletalAnimation::AnimatedJoint 
+
+declare SkeletalAnimation::AnimatedJoint 2
+  mJointName string
+  mKeyframes array instance SkeletalAnimation::Keyframe 
+
+declare SkeletalAnimation::Keyframe 3
+  mRotation quat
+  mTranslation vec3
+  mTime float
+
+object SkeletalAnimation 00000001
+  23
+      "hipsBone"
+      1
+          0.580969 0.662434 0.472893 0.005230
+          0.778002 0.123338 0.121363
+          0.000000
+      "R_Leg_sjnt_0"
+      1
+          -0.979544 0.029720 -0.196361 -0.032433
+          -0.104854 -0.000224 0.000080
+          0.000000
+      "R_Leg_sjnt_1"
+      1
+          0.607629 0.107933 -0.014707 0.786715
+          -0.000000 0.461809 0.000000
+          0.000000
+      "R_Foot_sjnt_0"
+      1
+          -0.601232 0.033798 -0.038697 0.797421
+          0.000000 0.388174 0.000000
+          0.000000
+      "L_Leg_sjnt_0"
+      1
+          0.047909 -0.102469 -0.016701 0.993441
+          0.104854 -0.000225 0.000080
+          0.000000
+      "L_Leg_sjnt_1"
+      1
+          -0.491497 -0.068754 -0.078845 -0.864573
+          0.000000 -0.461808 0.000000
+          0.000000
+      "L_Foot_sjnt_0"
+      1
+          0.390997 -0.045090 0.421830 -0.816791
+          -0.000001 -0.388173 -0.000000
+          0.000000
+      "C_Spine_sjnt_0"
+      1
+          0.031433 0.000503 0.001038 0.999505
+          -0.000000 0.055442 0.000000
+          0.000000
+      "C_Spine_sjnt_1"
+      1
+          0.062272 -0.003358 0.006714 0.998031
+          -0.000000 0.049000 0.000000
+          0.000000
+      "C_Spine_sjnt_2"
+      1
+          0.093197 -0.002900 0.010957 0.995583
+          0.000000 0.049000 -0.000000
+          0.000000
+      "C_Spine_sjnt_4"
+      1
+          -0.092643 0.004635 -0.011603 -0.995621
+          0.000000 0.098001 -0.000000
+          0.000000
+      "L_Clavicle_sjnt_0"
+      1
+          -0.850361 0.509199 0.108046 -0.077000
+          0.007299 0.208857 0.030677
+          0.000000
+      "L_Arm_sjnt_0"
+      1
+          0.622797 -0.313095 0.547896 0.462500
+          0.070455 -0.150703 0.005820
+          0.000000
+      "L_Arm_sjnt_1"
+      1
+          -0.570276 0.066909 0.307092 -0.758948
+          -0.000001 -0.260631 -0.000000
+          0.000000
+      "L_Wrist_sjnt_0"
+      1
+          0.107622 0.182086 0.068178 0.974994
+          0.000007 -0.242273 0.000002
+          0.000000
+      "R_Clavicle_sjnt_0"
+      1
+          -0.093524 0.097713 0.498124 0.856492
+          -0.007299 0.208857 0.030677
+          0.000000
+      "R_Arm_sjnt_0"
+      1
+          0.051326 0.583528 0.540453 0.603963
+          -0.070458 0.150703 -0.005820
+          0.000000
+      "R_Arm_sjnt_1"
+      1
+          -0.749170 0.055080 0.190723 -0.631930
+          -0.000001 0.260632 0.000000
+          0.000000
+      "R_Wrist_sjnt_0"
+      1
+          -0.000687 0.106883 -0.049184 0.993054
+          0.000000 0.242268 -0.000000
+          0.000000
+      "C_Neck_sjnt_0"
+      1
+          0.148136 -0.042494 0.052637 0.986651
+          -0.000000 0.206349 -0.038118
+          0.000000
+      "C_Neck_sjnt_1"
+      1
+          -0.272569 -0.021317 0.048735 0.960665
+          0.000000 0.054001 0.000000
+          0.000000
+      "C_Neck_sjnt_2"
+      1
+          -0.054186 0.008273 0.028994 0.998076
+          0.000000 0.054001 0.000000
+          0.000000
+      "C_Head_sjnt_0"
+      1
+          0.029319 -0.088467 0.137075 0.986167
+          0.000000 0.054000 -0.000000
+          0.000000

+ 131 - 0
Assets/Human/dead_pose3.tof

@@ -0,0 +1,131 @@
+TOS 1.00
+
+declare SkeletalAnimation 1
+  mAnimatedJoints array instance SkeletalAnimation::AnimatedJoint 
+
+declare SkeletalAnimation::AnimatedJoint 2
+  mJointName string
+  mKeyframes array instance SkeletalAnimation::Keyframe 
+
+declare SkeletalAnimation::Keyframe 3
+  mRotation quat
+  mTranslation vec3
+  mTime float
+
+object SkeletalAnimation 00000001
+  23
+      "hipsBone"
+      1
+          -0.253414 0.715602 -0.639670 0.120488
+          -0.113085 0.196785 -0.745374
+          0.000000
+      "R_Leg_sjnt_0"
+      1
+          -0.940855 0.027320 -0.183670 -0.283391
+          -0.104854 -0.000224 0.000080
+          0.000000
+      "R_Leg_sjnt_1"
+      1
+          0.732582 0.147108 -0.159629 0.645136
+          0.000000 0.461808 0.000000
+          0.000000
+      "R_Foot_sjnt_0"
+      1
+          -0.543008 -0.043463 0.072346 0.835476
+          0.000000 0.388174 0.000000
+          0.000000
+      "L_Leg_sjnt_0"
+      1
+          0.068071 -0.118484 0.183975 0.973386
+          0.104854 -0.000225 0.000080
+          0.000000
+      "L_Leg_sjnt_1"
+      1
+          0.258573 0.111198 0.041401 0.958677
+          0.000000 -0.461808 0.000000
+          0.000000
+      "L_Foot_sjnt_0"
+      1
+          -0.388386 -0.112771 -0.101140 0.908961
+          -0.000001 -0.388173 -0.000000
+          0.000000
+      "C_Spine_sjnt_0"
+      1
+          0.055573 0.116937 0.111430 0.985302
+          0.000000 0.055442 0.000000
+          0.000000
+      "C_Spine_sjnt_1"
+      1
+          0.037873 -0.006875 0.039870 0.998463
+          0.000000 0.049000 0.000000
+          0.000000
+      "C_Spine_sjnt_2"
+      1
+          0.057513 -0.003345 0.060246 0.996520
+          -0.000000 0.049000 -0.000000
+          0.000000
+      "C_Spine_sjnt_4"
+      1
+          0.058450 0.004523 0.059058 0.996532
+          0.000000 0.098001 0.000000
+          0.000000
+      "L_Clavicle_sjnt_0"
+      1
+          -0.788890 0.552170 0.262911 0.060331
+          0.007299 0.208857 0.030677
+          0.000000
+      "L_Arm_sjnt_0"
+      1
+          -0.242065 -0.166972 -0.682243 -0.669381
+          0.070455 -0.150703 0.005819
+          0.000000
+      "L_Arm_sjnt_1"
+      1
+          0.293529 -0.405618 0.091549 0.860775
+          -0.000001 -0.260631 0.000000
+          0.000000
+      "L_Wrist_sjnt_0"
+      1
+          -0.018010 -0.348798 0.050741 0.935650
+          0.000007 -0.242273 0.000002
+          0.000000
+      "R_Clavicle_sjnt_0"
+      1
+          0.006775 0.017839 0.047960 0.998667
+          -0.007299 0.208857 0.030677
+          0.000000
+      "R_Arm_sjnt_0"
+      1
+          -0.057684 0.616002 0.098229 0.779464
+          -0.070458 0.150703 -0.005820
+          0.000000
+      "R_Arm_sjnt_1"
+      1
+          0.360633 -0.148932 -0.172766 0.904387
+          -0.000001 0.260632 -0.000000
+          0.000000
+      "R_Wrist_sjnt_0"
+      1
+          -0.073581 -0.376053 0.364024 -0.848915
+          0.000000 0.242268 -0.000000
+          0.000000
+      "C_Neck_sjnt_0"
+      1
+          0.208406 0.070974 -0.020180 0.975255
+          -0.000000 0.206349 -0.038118
+          0.000000
+      "C_Neck_sjnt_1"
+      1
+          -0.214167 0.070851 -0.015228 0.974105
+          -0.000000 0.054000 0.000000
+          0.000000
+      "C_Neck_sjnt_2"
+      1
+          0.013069 0.069734 0.004440 0.997470
+          0.000000 0.054001 0.000000
+          0.000000
+      "C_Head_sjnt_0"
+      1
+          -0.011944 -0.296295 0.045126 0.953955
+          0.000000 0.054000 0.000000
+          0.000000

+ 131 - 0
Assets/Human/dead_pose4.tof

@@ -0,0 +1,131 @@
+TOS 1.00
+
+declare SkeletalAnimation 1
+  mAnimatedJoints array instance SkeletalAnimation::AnimatedJoint 
+
+declare SkeletalAnimation::AnimatedJoint 2
+  mJointName string
+  mKeyframes array instance SkeletalAnimation::Keyframe 
+
+declare SkeletalAnimation::Keyframe 3
+  mRotation quat
+  mTranslation vec3
+  mTime float
+
+object SkeletalAnimation 00000001
+  23
+      "hipsBone"
+      1
+          -0.006100 0.596243 0.801570 0.044078
+          0.158819 0.119626 0.352148
+          0.000000
+      "R_Leg_sjnt_0"
+      1
+          0.987181 -0.088292 0.091403 0.096563
+          -0.104854 -0.000224 0.000080
+          0.000000
+      "R_Leg_sjnt_1"
+      1
+          0.518738 0.091591 -0.028278 0.849542
+          -0.000000 0.461809 -0.000000
+          0.000000
+      "R_Foot_sjnt_0"
+      1
+          -0.446222 -0.027605 0.081585 0.890768
+          0.000000 0.388174 0.000000
+          0.000000
+      "L_Leg_sjnt_0"
+      1
+          -0.004252 0.099688 0.099358 0.990036
+          0.104854 -0.000225 0.000080
+          0.000000
+      "L_Leg_sjnt_1"
+      1
+          0.332849 0.214890 0.078034 0.914847
+          0.000000 -0.461808 0.000000
+          0.000000
+      "L_Foot_sjnt_0"
+      1
+          -0.353947 -0.242590 0.059009 0.901326
+          -0.000001 -0.388173 -0.000000
+          0.000000
+      "C_Spine_sjnt_0"
+      1
+          0.040410 -0.000191 -0.035615 0.998548
+          0.000000 0.055442 -0.000000
+          0.000000
+      "C_Spine_sjnt_1"
+      1
+          0.078135 -0.005104 -0.037889 0.996210
+          0.000000 0.049000 0.000000
+          0.000000
+      "C_Spine_sjnt_2"
+      1
+          0.117588 0.001930 -0.056543 0.991450
+          -0.000000 0.049000 0.000000
+          0.000000
+      "C_Spine_sjnt_4"
+      1
+          0.116613 0.022537 -0.060157 0.991098
+          0.000000 0.098001 -0.000000
+          0.000000
+      "L_Clavicle_sjnt_0"
+      1
+          0.820798 -0.461866 -0.218872 0.255080
+          0.007299 0.208857 0.030677
+          0.000000
+      "L_Arm_sjnt_0"
+      1
+          -0.018640 0.550280 0.313113 0.773825
+          0.070455 -0.150703 0.005819
+          0.000000
+      "L_Arm_sjnt_1"
+      1
+          0.502820 -0.083351 -0.423467 0.748933
+          -0.000001 -0.260631 0.000000
+          0.000000
+      "L_Wrist_sjnt_0"
+      1
+          -0.127266 -0.436122 0.115708 -0.883296
+          0.000007 -0.242273 0.000002
+          0.000000
+      "R_Clavicle_sjnt_0"
+      1
+          -0.254409 -0.049344 0.561585 0.785789
+          -0.007299 0.208857 0.030677
+          0.000000
+      "R_Arm_sjnt_0"
+      1
+          -0.297704 0.588370 -0.433881 -0.613954
+          -0.070457 0.150703 -0.005820
+          0.000000
+      "R_Arm_sjnt_1"
+      1
+          0.300035 -0.109892 -0.134453 0.937990
+          -0.000001 0.260632 -0.000000
+          0.000000
+      "R_Wrist_sjnt_0"
+      1
+          -0.074182 0.083861 -0.170265 0.979017
+          0.000000 0.242268 -0.000000
+          0.000000
+      "C_Neck_sjnt_0"
+      1
+          0.095959 0.024101 0.021582 0.994859
+          0.000000 0.206349 -0.038118
+          0.000000
+      "C_Neck_sjnt_1"
+      1
+          -0.330366 0.030108 0.013511 0.943276
+          0.000000 0.054001 0.000000
+          0.000000
+      "C_Neck_sjnt_2"
+      1
+          -0.116595 0.020825 0.025026 0.992646
+          0.000000 0.054001 0.000000
+          0.000000
+      "C_Head_sjnt_0"
+      1
+          -0.225003 0.005643 -0.043020 0.973392
+          -0.000000 0.054000 0.000000
+          0.000000

+ 131 - 0
Assets/Human/neutral.tof

@@ -0,0 +1,131 @@
+TOS 1.00
+
+declare SkeletalAnimation 1
+  mAnimatedJoints array instance SkeletalAnimation::AnimatedJoint 
+
+declare SkeletalAnimation::AnimatedJoint 2
+  mJointName string
+  mKeyframes array instance SkeletalAnimation::Keyframe 
+
+declare SkeletalAnimation::Keyframe 3
+  mRotation quat
+  mTranslation vec3
+  mTime float
+
+object SkeletalAnimation 00000001
+  23
+      "hipsBone"
+      1
+          0.000000 1.000000 0.000000 0.000000
+          0.000000 0.931122 -0.023035
+          0.000000
+      "R_Leg_sjnt_0"
+      1
+          0.999643 -0.023767 -0.000290 0.012213
+          -0.104854 -0.000224 0.000080
+          0.000000
+      "R_Leg_sjnt_1"
+      1
+          0.060151 0.000000 -0.000000 0.998189
+          -0.000000 0.461809 -0.000000
+          0.000000
+      "R_Foot_sjnt_0"
+      1
+          -0.546992 0.011039 -0.021050 0.836800
+          0.000000 0.388174 0.000000
+          0.000000
+      "L_Leg_sjnt_0"
+      1
+          -0.012213 -0.000290 0.023767 0.999643
+          0.104854 -0.000225 0.000080
+          0.000000
+      "L_Leg_sjnt_1"
+      1
+          0.060151 0.000000 -0.000000 0.998189
+          0.000000 -0.461807 0.000000
+          0.000000
+      "L_Foot_sjnt_0"
+      1
+          -0.546992 0.011039 -0.021050 0.836800
+          -0.000001 -0.388173 -0.000000
+          0.000000
+      "C_Spine_sjnt_0"
+      1
+          0.000000 0.000000 0.000000 1.000000
+          0.000000 0.055442 -0.000000
+          0.000000
+      "C_Spine_sjnt_1"
+      1
+          0.000000 0.000000 0.000000 1.000000
+          0.000000 0.049000 0.000000
+          0.000000
+      "C_Spine_sjnt_2"
+      1
+          0.000000 0.000000 0.000000 1.000000
+          0.000000 0.049000 -0.000000
+          0.000000
+      "C_Spine_sjnt_4"
+      1
+          0.000000 0.000000 0.000000 1.000000
+          0.000000 0.098001 0.000000
+          0.000000
+      "L_Clavicle_sjnt_0"
+      1
+          0.761695 -0.617813 -0.144681 0.131128
+          0.007299 0.208857 0.030677
+          0.000000
+      "L_Arm_sjnt_0"
+      1
+          0.199254 -0.056730 0.463827 0.861362
+          0.070456 -0.150703 0.005820
+          0.000000
+      "L_Arm_sjnt_1"
+      1
+          0.184809 -0.000000 -0.000000 0.982774
+          -0.000001 -0.260631 0.000000
+          0.000000
+      "L_Wrist_sjnt_0"
+      1
+          -0.000000 0.000000 0.000000 1.000000
+          0.000007 -0.242273 0.000002
+          0.000000
+      "R_Clavicle_sjnt_0"
+      1
+          -0.131128 -0.144681 0.617813 0.761696
+          -0.007299 0.208857 0.030677
+          0.000000
+      "R_Arm_sjnt_0"
+      1
+          0.199254 -0.056730 0.463827 0.861362
+          -0.070458 0.150703 -0.005820
+          0.000000
+      "R_Arm_sjnt_1"
+      1
+          0.184809 0.000000 0.000000 0.982774
+          -0.000001 0.260632 -0.000000
+          0.000000
+      "R_Wrist_sjnt_0"
+      1
+          -0.000000 0.000000 -0.000000 1.000000
+          0.000000 0.242268 -0.000000
+          0.000000
+      "C_Neck_sjnt_0"
+      1
+          0.216440 0.000000 0.000000 0.976296
+          0.000000 0.206349 -0.038118
+          0.000000
+      "C_Neck_sjnt_1"
+      1
+          -0.216440 0.000000 0.000000 0.976296
+          0.000000 0.054001 0.000000
+          0.000000
+      "C_Neck_sjnt_2"
+      1
+          0.000000 0.000000 0.000000 1.000000
+          0.000000 0.054001 0.000000
+          0.000000
+      "C_Head_sjnt_0"
+      1
+          0.000000 0.000000 0.000000 1.000000
+          0.000000 0.054000 -0.000000
+          0.000000

+ 3443 - 0
Assets/Human/sprint.tof

@@ -0,0 +1,3443 @@
+TOS 1.00
+
+declare SkeletalAnimation 1
+  mAnimatedJoints array instance SkeletalAnimation::AnimatedJoint 
+
+declare SkeletalAnimation::AnimatedJoint 2
+  mJointName string
+  mKeyframes array instance SkeletalAnimation::Keyframe 
+
+declare SkeletalAnimation::Keyframe 3
+  mRotation quat
+  mTranslation vec3
+  mTime float
+
+object SkeletalAnimation 00000001
+  23
+      "hipsBone"
+      49
+          0.076756 0.971015 -0.198088 -0.109541
+          0.006216 0.755616 0.082018
+          0.000000
+          0.075293 0.965522 -0.205198 -0.141396
+          0.010054 0.754882 -0.102516
+          0.033333
+          0.031435 0.969573 -0.215463 -0.111877
+          0.011457 0.761961 -0.297186
+          0.066667
+          -0.018036 0.969179 -0.239930 -0.052921
+          0.002153 0.777095 -0.497473
+          0.100000
+          -0.029957 0.966926 -0.253041 0.011306
+          -0.017702 0.791988 -0.692753
+          0.133333
+          -0.019257 0.969927 -0.236362 0.054816
+          -0.035148 0.802004 -0.879850
+          0.166667
+          -0.013737 0.973288 -0.207094 0.098153
+          -0.044904 0.797372 -1.061694
+          0.200000
+          -0.024877 0.971291 -0.189041 0.142264
+          -0.045386 0.770026 -1.274826
+          0.233333
+          -0.032306 0.963387 -0.189080 0.187327
+          -0.044646 0.746584 -1.481218
+          0.266667
+          -0.019274 0.958233 -0.183928 0.218147
+          -0.044394 0.733642 -1.691272
+          0.300000
+          0.009389 0.968092 -0.163919 0.189317
+          -0.043175 0.722412 -1.929407
+          0.333333
+          0.053510 0.972922 -0.190155 0.120001
+          -0.034635 0.738034 -2.162788
+          0.366667
+          0.062064 0.973231 -0.213942 0.056551
+          -0.017801 0.760006 -2.383472
+          0.400000
+          0.040314 0.978976 -0.199314 0.015975
+          -0.004141 0.777593 -2.599022
+          0.433333
+          0.038850 0.982356 -0.181805 -0.020369
+          0.003405 0.779307 -2.809443
+          0.466667
+          0.061903 0.979939 -0.179044 -0.061890
+          0.004118 0.763678 -3.011809
+          0.500000
+          0.076843 0.971018 -0.198090 -0.109454
+          0.006216 0.755616 -3.197294
+          0.533333
+          0.075206 0.965456 -0.205478 -0.141482
+          0.010054 0.754882 -3.381834
+          0.566667
+          0.031521 0.969547 -0.215610 -0.111791
+          0.011457 0.761961 -3.576509
+          0.600000
+          -0.017864 0.969181 -0.239933 -0.052921
+          0.002153 0.777095 -3.776801
+          0.633333
+          -0.029870 0.966964 -0.252907 0.011220
+          -0.017702 0.791988 -3.972086
+          0.666667
+          -0.019257 0.969976 -0.236238 0.054471
+          -0.035148 0.802004 -4.159187
+          0.700000
+          -0.013391 0.973394 -0.207027 0.097289
+          -0.044904 0.797372 -4.341038
+          0.733334
+          -0.024446 0.971542 -0.188775 0.140970
+          -0.045386 0.770026 -4.554170
+          0.766667
+          -0.032047 0.963632 -0.188981 0.186205
+          -0.044646 0.746583 -4.760562
+          0.800000
+          -0.019361 0.958221 -0.184087 0.218060
+          -0.044394 0.733642 -4.970616
+          0.833334
+          0.009303 0.968132 -0.163787 0.189230
+          -0.043175 0.722412 -5.208751
+          0.866667
+          0.053338 0.972948 -0.190181 0.119827
+          -0.034635 0.738034 -5.442133
+          0.900000
+          0.061804 0.973218 -0.214101 0.056464
+          -0.017801 0.760006 -5.662817
+          0.933334
+          0.040140 0.978982 -0.199320 0.015974
+          -0.004141 0.777593 -5.878366
+          0.966667
+          0.038763 0.982360 -0.181809 -0.020283
+          0.003405 0.779306 -6.088788
+          1.000000
+          0.061730 0.979949 -0.179053 -0.061890
+          0.004118 0.763678 -6.291152
+          1.033334
+          0.076756 0.971002 -0.198247 -0.109368
+          0.006216 0.755616 -6.477829
+          1.066667
+          0.075206 0.965456 -0.205478 -0.141482
+          0.010054 0.754882 -6.662371
+          1.100000
+          0.031521 0.969578 -0.215469 -0.111791
+          0.011457 0.761961 -6.857046
+          1.133334
+          -0.017951 0.969176 -0.239928 -0.053006
+          0.002153 0.777095 -7.057342
+          1.166667
+          -0.029870 0.966929 -0.253044 0.011220
+          -0.017702 0.791988 -7.252628
+          1.200000
+          -0.019257 0.969942 -0.236377 0.054471
+          -0.035148 0.802004 -7.439733
+          1.233333
+          -0.013564 0.973361 -0.207167 0.097289
+          -0.044904 0.797372 -7.621584
+          1.266667
+          -0.024619 0.971510 -0.188916 0.140970
+          -0.045386 0.770026 -7.834717
+          1.300000
+          -0.032133 0.963644 -0.188993 0.186118
+          -0.044646 0.746584 -8.041104
+          1.333333
+          -0.019361 0.958248 -0.183943 0.218061
+          -0.044394 0.733642 -8.251154
+          1.366667
+          0.009389 0.968117 -0.163772 0.189317
+          -0.043175 0.722412 -8.489285
+          1.400000
+          0.053424 0.972935 -0.190168 0.119915
+          -0.034635 0.738033 -8.722664
+          1.433333
+          0.061976 0.973200 -0.214084 0.056638
+          -0.017801 0.760006 -8.943342
+          1.466667
+          0.040313 0.978971 -0.199309 0.016321
+          -0.004141 0.777593 -9.158888
+          1.500000
+          0.038849 0.982400 -0.181677 -0.019333
+          0.003405 0.779306 -9.369305
+          1.533333
+          0.061817 0.980021 -0.178952 -0.060940
+          0.004118 0.763679 -9.571669
+          1.566666
+          0.076756 0.971015 -0.198088 -0.109541
+          0.006216 0.755616 -9.757915
+          1.600000
+      "R_Leg_sjnt_0"
+      49
+          0.948340 0.016562 0.212514 0.234978
+          -0.104854 -0.000224 0.000080
+          0.000000
+          0.880623 0.019500 0.182715 0.436736
+          -0.104854 -0.000224 0.000080
+          0.033333
+          0.776781 0.027092 0.085500 0.623351
+          -0.104854 -0.000224 0.000080
+          0.066667
+          0.707114 0.052259 0.018089 0.704934
+          -0.104854 -0.000224 0.000080
+          0.100000
+          0.716569 0.062521 -0.006823 0.694675
+          -0.104854 -0.000224 0.000080
+          0.133333
+          0.786543 0.076924 0.012233 0.612603
+          -0.104854 -0.000224 0.000080
+          0.166667
+          0.823779 0.104020 0.060602 0.553981
+          -0.104854 -0.000224 0.000080
+          0.200000
+          0.844437 0.111344 0.065978 0.519784
+          -0.104854 -0.000224 0.000080
+          0.233333
+          0.886754 0.084738 0.008816 0.454323
+          -0.104854 -0.000224 0.000080
+          0.266667
+          0.943851 0.014905 -0.028815 0.328774
+          -0.104854 -0.000224 0.000080
+          0.300000
+          -0.989273 0.086933 0.005876 -0.117245
+          -0.104854 -0.000224 0.000080
+          0.333333
+          -0.990507 0.123814 -0.023916 0.054714
+          -0.104854 -0.000224 0.000080
+          0.366667
+          -0.984363 0.049315 0.064490 0.156326
+          -0.104854 -0.000224 0.000080
+          0.400000
+          -0.991180 -0.005126 -0.011731 0.131901
+          -0.104854 -0.000224 0.000080
+          0.433333
+          -0.993050 -0.009258 -0.105039 0.052272
+          -0.104854 -0.000224 0.000080
+          0.466667
+          -0.984026 -0.005582 -0.166591 -0.062530
+          -0.104854 -0.000224 0.000080
+          0.500000
+          0.948072 0.021935 0.211050 0.236932
+          -0.104854 -0.000224 0.000080
+          0.533333
+          0.880848 0.024873 0.179785 0.437225
+          -0.104854 -0.000224 0.000080
+          0.566667
+          0.779198 0.022213 0.061562 0.623351
+          -0.104854 -0.000224 0.000080
+          0.600000
+          0.707493 0.040056 -0.015132 0.705422
+          -0.104854 -0.000224 0.000080
+          0.633333
+          0.716474 0.053733 -0.022946 0.695163
+          -0.104854 -0.000224 0.000080
+          0.666667
+          0.786560 0.076680 0.012722 0.612603
+          -0.104854 -0.000224 0.000080
+          0.700000
+          0.824133 0.103531 0.061091 0.553492
+          -0.104854 -0.000224 0.000081
+          0.733334
+          0.844828 0.110367 0.066466 0.519296
+          -0.104854 -0.000223 0.000080
+          0.766667
+          0.886597 0.083761 0.008815 0.454811
+          -0.104854 -0.000224 0.000080
+          0.800000
+          0.943310 0.014904 -0.029792 0.330237
+          -0.104854 -0.000224 0.000080
+          0.833334
+          -0.989012 0.087177 0.006853 -0.119198
+          -0.104854 -0.000224 0.000080
+          0.866667
+          -0.990706 0.125034 -0.021961 0.048852
+          -0.104854 -0.000225 0.000081
+          0.900000
+          -0.988276 0.048825 0.067420 0.127992
+          -0.104854 -0.000224 0.000079
+          0.933334
+          -0.993177 -0.002196 -0.008800 0.116267
+          -0.104854 -0.000224 0.000080
+          0.966667
+          -0.993700 -0.005839 -0.101619 0.046897
+          -0.104854 -0.000223 0.000080
+          1.000000
+          -0.984486 -0.002164 -0.163171 -0.064486
+          -0.104854 -0.000224 0.000080
+          1.033334
+          0.947967 0.017540 0.208606 0.239865
+          -0.104854 -0.000223 0.000080
+          1.066667
+          0.879305 0.018524 0.177340 0.441623
+          -0.104854 -0.000224 0.000080
+          1.100000
+          0.778392 0.014643 0.059117 0.624817
+          -0.104854 -0.000224 0.000079
+          1.133334
+          0.710182 0.034196 -0.017088 0.702980
+          -0.104854 -0.000224 0.000081
+          1.166667
+          0.721914 0.048849 -0.025389 0.689789
+          -0.104854 -0.000224 0.000080
+          1.200000
+          0.796674 0.073262 0.007836 0.599902
+          -0.104854 -0.000224 0.000081
+          1.233333
+          0.822398 0.106951 0.056206 0.555935
+          -0.104854 -0.000224 0.000080
+          1.266667
+          0.841985 0.115252 0.063536 0.523204
+          -0.104854 -0.000224 0.000080
+          1.300000
+          0.887177 0.085716 0.006862 0.453346
+          -0.104854 -0.000224 0.000080
+          1.333333
+          0.943103 0.015150 -0.030769 0.330728
+          -0.104854 -0.000224 0.000080
+          1.366667
+          -0.989012 0.087176 0.006853 -0.119200
+          -0.104854 -0.000224 0.000080
+          1.400000
+          -0.990696 0.125767 -0.022450 0.046897
+          -0.104854 -0.000224 0.000080
+          1.433333
+          -0.988211 0.049559 0.065955 0.128969
+          -0.104854 -0.000224 0.000080
+          1.466667
+          -0.992564 -0.001218 -0.011729 0.121153
+          -0.104854 -0.000225 0.000080
+          1.500000
+          -0.993109 -0.003641 -0.106014 0.049830
+          -0.104854 -0.000224 0.000079
+          1.533333
+          -0.983990 -0.000454 -0.167078 -0.062041
+          -0.104854 -0.000224 0.000080
+          1.566666
+          0.948341 0.016562 0.212514 0.234976
+          -0.104854 -0.000224 0.000081
+          1.600000
+      "R_Leg_sjnt_1"
+      49
+          -0.926504 -0.020038 -0.051270 -0.372237
+          -0.000000 0.461809 -0.000000
+          0.000000
+          -0.950921 -0.018328 -0.058108 -0.303376
+          -0.000000 0.461808 -0.000000
+          0.033333
+          -0.913563 -0.022235 -0.047363 -0.403318
+          -0.000000 0.461809 -0.000000
+          0.066667
+          -0.789036 -0.023944 -0.028313 -0.613227
+          -0.000000 0.461809 -0.000000
+          0.100000
+          -0.570016 -0.006608 -0.002819 -0.821602
+          -0.000000 0.461809 0.000000
+          0.133333
+          0.331218 0.000978 -0.001583 0.943552
+          -0.000000 0.461809 -0.000000
+          0.166667
+          0.346478 -0.002889 -0.002743 0.938050
+          -0.000000 0.461809 -0.000000
+          0.200000
+          -0.523990 0.009871 0.007715 -0.851632
+          -0.000000 0.461809 -0.000000
+          0.233333
+          -0.624101 0.015359 0.014549 -0.781057
+          -0.000000 0.461809 0.000000
+          0.266667
+          -0.626421 0.029273 0.025416 -0.778520
+          -0.000000 0.461809 -0.000000
+          0.300000
+          0.520695 -0.040995 -0.026398 0.852349
+          -0.000000 0.461809 0.000000
+          0.333333
+          0.302528 -0.012076 -0.004790 0.953052
+          -0.000000 0.461808 -0.000000
+          0.366667
+          0.180807 0.045412 0.007793 0.982439
+          -0.000000 0.461808 -0.000000
+          0.400000
+          0.373456 0.084964 0.033684 0.923134
+          -0.000000 0.461809 -0.000000
+          0.433333
+          0.636428 0.059091 0.049310 0.767487
+          -0.000000 0.461809 -0.000000
+          0.466667
+          0.830300 0.026873 0.041011 0.555155
+          -0.000000 0.461808 0.000000
+          0.500000
+          -0.928824 -0.014425 -0.038085 -0.368277
+          -0.000000 0.461808 -0.000000
+          0.533333
+          -0.953729 -0.010155 -0.032715 -0.298709
+          -0.000000 0.461808 0.000000
+          0.566667
+          -0.915639 -0.009058 -0.017819 -0.401505
+          -0.000000 0.461809 0.000000
+          0.600000
+          -0.790867 -0.012106 -0.013296 -0.611723
+          -0.000000 0.461808 -0.000000
+          0.633333
+          -0.569771 -0.009171 -0.004559 -0.821739
+          -0.000000 0.461809 0.000000
+          0.666667
+          0.331339 0.030280 0.008647 0.942986
+          -0.000000 0.461809 0.000000
+          0.700000
+          0.344158 0.029792 0.009135 0.938394
+          -0.000000 0.461809 0.000001
+          0.733334
+          -0.520205 -0.023450 -0.012678 -0.853625
+          -0.000000 0.461808 0.000000
+          0.766667
+          -0.622146 -0.022476 -0.015611 -0.782423
+          -0.000000 0.461809 0.000000
+          0.800000
+          -0.626174 -0.014177 -0.009383 -0.779498
+          -0.000000 0.461808 0.000000
+          0.833334
+          0.522645 0.004287 0.001184 0.852539
+          0.000000 0.461809 0.000000
+          0.866667
+          0.316445 -0.003806 -0.002347 0.948600
+          -0.000000 0.461809 0.000000
+          0.900000
+          0.246980 -0.000893 -0.000944 0.969020
+          -0.000000 0.461809 -0.000001
+          0.933334
+          0.409108 0.058111 0.025378 0.910280
+          -0.000000 0.461808 -0.000001
+          0.966667
+          0.650105 0.036146 0.030507 0.758371
+          -0.000000 0.461808 0.000000
+          1.000000
+          0.836650 0.011985 0.017815 0.547316
+          -0.000000 0.461810 0.000001
+          1.033334
+          -0.931998 -0.013938 -0.035156 -0.360485
+          -0.000000 0.461810 0.000001
+          1.066667
+          -0.954706 -0.014059 -0.043947 -0.293953
+          -0.000000 0.461808 0.000000
+          1.100000
+          -0.913807 -0.009913 -0.017819 -0.405636
+          0.000000 0.461808 0.000000
+          1.133334
+          -0.781711 -0.007835 -0.006275 -0.623560
+          0.000000 0.461809 -0.000001
+          1.166667
+          -0.547917 -0.010757 -0.004497 -0.836451
+          -0.000000 0.461808 0.000000
+          1.200000
+          0.288365 0.025885 0.005776 0.957153
+          -0.000000 0.461809 -0.000001
+          1.233333
+          0.336345 0.013802 0.003059 0.941633
+          0.000000 0.461808 -0.000001
+          1.266667
+          -0.519594 -0.016127 -0.008038 -0.854223
+          -0.000000 0.461808 0.000000
+          1.300000
+          -0.616408 -0.012834 -0.007674 -0.787285
+          -0.000000 0.461808 0.000001
+          1.333333
+          -0.624222 -0.002216 0.000305 -0.781244
+          -0.000001 0.461809 0.000000
+          1.366667
+          0.522037 -0.003036 -0.003380 0.852911
+          -0.000000 0.461807 -0.000001
+          1.400000
+          0.321940 0.001031 -0.000805 0.946759
+          0.000000 0.461808 -0.000001
+          1.433333
+          0.244415 0.007574 0.001209 0.969640
+          -0.000000 0.461808 -0.000001
+          1.466667
+          0.397508 0.023446 0.009501 0.917250
+          -0.000000 0.461808 0.000000
+          1.500000
+          0.641921 0.028090 0.023669 0.765890
+          0.000000 0.461808 -0.000001
+          1.533333
+          0.830299 0.021015 0.031732 0.556016
+          0.000000 0.461810 0.000001
+          1.566666
+          -0.926503 -0.020038 -0.051270 -0.372239
+          -0.000000 0.461808 0.000000
+          1.600000
+      "R_Foot_sjnt_0"
+      49
+          -0.638990 0.081067 0.074265 0.761318
+          0.000000 0.388174 0.000000
+          0.000000
+          -0.710314 0.073238 0.108457 0.691612
+          0.000000 0.388174 0.000000
+          0.033333
+          0.713732 -0.087910 -0.032739 -0.694108
+          0.000000 0.388174 0.000000
+          0.066667
+          0.643386 -0.103547 -0.033723 -0.757756
+          0.000000 0.388174 0.000000
+          0.100000
+          0.587205 -0.079131 -0.004409 -0.805549
+          0.000000 0.388174 0.000000
+          0.133333
+          -0.500732 0.019063 -0.062528 0.863131
+          0.000000 0.388174 0.000000
+          0.166667
+          -0.500241 -0.040543 -0.032740 0.864317
+          0.000000 0.388174 0.000000
+          0.200000
+          0.646799 0.063531 -0.056159 -0.757932
+          0.000000 0.388174 0.000000
+          0.233333
+          0.753784 0.043012 -0.011703 -0.655608
+          0.000000 0.388174 0.000000
+          0.266667
+          0.797260 0.056684 0.035200 -0.599936
+          0.000000 0.388173 0.000000
+          0.300000
+          0.786999 0.075235 0.090406 -0.605639
+          0.000000 0.388174 0.000000
+          0.333333
+          0.608203 0.015134 0.092339 -0.788247
+          0.000000 0.388174 0.000000
+          0.366667
+          -0.374210 0.103089 -0.032217 0.921033
+          0.000000 0.388174 0.000000
+          0.400000
+          -0.423053 -0.071309 -0.058147 0.901421
+          0.000000 0.388174 0.000000
+          0.433333
+          -0.499752 -0.040539 -0.050327 0.863754
+          0.000000 0.388174 0.000000
+          0.466667
+          -0.588668 0.060573 -0.020021 0.805853
+          0.000000 0.388174 0.000000
+          0.500000
+          -0.656087 0.072278 0.050814 0.749496
+          0.000000 0.388174 0.000000
+          0.533333
+          -0.729365 0.048816 0.085979 0.676943
+          0.000000 0.388174 0.000000
+          0.566667
+          0.729363 -0.060069 -0.007330 -0.681445
+          0.000000 0.388174 0.000000
+          0.600000
+          0.653155 -0.093295 0.000965 -0.751454
+          0.000000 0.388174 0.000000
+          0.633333
+          0.588669 -0.057143 -0.024433 -0.805981
+          0.000000 0.388174 0.000000
+          0.666667
+          -0.505129 0.024434 -0.051291 0.861172
+          0.000000 0.388174 0.000000
+          0.700000
+          -0.507082 -0.033217 -0.022968 0.860951
+          0.000000 0.388174 0.000000
+          0.733334
+          0.654128 0.040082 -0.051278 -0.753578
+          0.000000 0.388174 0.000000
+          0.766667
+          0.757694 0.003925 0.013204 -0.652465
+          0.000000 0.388175 0.000000
+          0.800000
+          0.797261 0.007335 0.073783 -0.599063
+          0.000000 0.388175 0.000000
+          0.833334
+          0.783580 0.023445 0.123614 -0.608418
+          0.000000 0.388175 0.000000
+          0.866667
+          0.616996 0.008300 0.067423 -0.784029
+          0.000000 0.388174 0.000000
+          0.900000
+          -0.395216 0.079146 -0.018544 0.914984
+          0.000000 0.388173 0.000000
+          0.933334
+          -0.407421 -0.086457 -0.039097 0.908298
+          0.000000 0.388175 0.000000
+          0.966667
+          -0.482166 -0.070833 -0.013693 0.873104
+          0.000000 0.388174 0.000000
+          1.000000
+          -0.585738 0.023433 0.038105 0.809265
+          0.000000 0.388175 0.000000
+          1.033334
+          -0.665859 0.071779 0.095268 0.736480
+          0.000000 0.388173 0.000000
+          1.066667
+          -0.751350 0.091317 0.078170 0.648863
+          0.000000 0.388174 0.000000
+          1.100000
+          0.754767 -0.098173 -0.000986 -0.648604
+          0.000000 0.388174 0.000000
+          1.133334
+          0.677094 -0.115762 -0.013206 -0.726614
+          0.000000 0.388173 0.000001
+          1.166667
+          0.603327 -0.133851 0.026357 -0.785739
+          0.000000 0.388175 0.000001
+          1.200000
+          -0.499758 0.105049 -0.094263 0.854588
+          0.000000 0.388174 0.000002
+          1.233333
+          -0.518319 -0.000001 -0.011726 0.855107
+          0.000000 0.388174 0.000000
+          1.266667
+          0.665364 0.026407 -0.064471 -0.743261
+          0.000000 0.388174 0.000000
+          1.300000
+          0.771861 -0.009266 0.023460 -0.635291
+          -0.000000 0.388174 0.000001
+          1.333333
+          0.801170 -0.000972 0.084528 -0.592437
+          -0.000000 0.388175 0.000001
+          1.366667
+          0.786023 0.021490 0.126545 -0.604725
+          0.000000 0.388172 0.000000
+          1.400000
+          0.604786 -0.009294 0.100639 -0.789949
+          0.000000 0.388172 0.000000
+          1.433333
+          -0.358089 0.122152 -0.079599 0.922234
+          0.000000 0.388172 0.000000
+          1.466667
+          -0.361498 -0.072758 -0.114325 0.922472
+          0.000000 0.388177 0.000000
+          1.500000
+          -0.430869 -0.051279 -0.065960 0.898538
+          -0.000000 0.388175 -0.000000
+          1.533333
+          -0.551051 0.054710 -0.010739 0.832607
+          0.000000 0.388172 0.000000
+          1.566666
+          -0.638989 0.081067 0.074265 0.761318
+          0.000000 0.388174 0.000001
+          1.600000
+      "L_Leg_sjnt_0"
+      49
+          -0.414794 0.060835 -0.122488 0.899579
+          0.104854 -0.000225 0.000080
+          0.000000
+          -0.305351 0.026124 -0.070255 0.949285
+          0.104854 -0.000225 0.000080
+          0.033333
+          -0.146068 0.047099 0.023499 0.987873
+          0.104854 -0.000225 0.000080
+          0.066667
+          0.030787 0.043181 0.058622 0.996871
+          0.104854 -0.000225 0.000080
+          0.100000
+          0.137756 -0.024708 -0.000046 0.990158
+          0.104854 -0.000225 0.000080
+          0.133333
+          0.115752 -0.012968 -0.072338 0.990556
+          0.104854 -0.000225 0.000080
+          0.166667
+          0.050286 0.106723 -0.086949 0.989202
+          0.104854 -0.000225 0.000080
+          0.200000
+          -0.028364 0.202964 -0.081043 0.975414
+          0.104854 -0.000225 0.000080
+          0.233333
+          -0.172480 0.253779 -0.088807 0.947608
+          0.104854 -0.000225 0.000080
+          0.266667
+          -0.373749 0.214711 -0.085827 0.898245
+          0.104854 -0.000225 0.000080
+          0.300000
+          -0.554002 0.093083 -0.053071 0.825591
+          0.104854 -0.000225 0.000080
+          0.333333
+          -0.675153 -0.000200 -0.049149 0.736038
+          0.104854 -0.000225 0.000080
+          0.366667
+          -0.691765 -0.012407 -0.057940 0.719687
+          0.104854 -0.000225 0.000080
+          0.400000
+          -0.635592 0.033505 -0.078953 0.767246
+          0.104854 -0.000225 0.000080
+          0.433333
+          -0.559881 0.096518 -0.109737 0.815583
+          0.104854 -0.000225 0.000080
+          0.466667
+          -0.482213 0.103838 -0.132230 0.859769
+          0.104854 -0.000225 0.000080
+          0.500000
+          -0.414794 0.060835 -0.122488 0.899579
+          0.104854 -0.000225 0.000080
+          0.533333
+          -0.305840 0.026124 -0.070255 0.949128
+          0.104854 -0.000225 0.000080
+          0.566667
+          -0.146067 0.047099 0.023011 0.987885
+          0.104854 -0.000225 0.000080
+          0.600000
+          0.030787 0.043181 0.058621 0.996871
+          0.104854 -0.000225 0.000080
+          0.633333
+          0.137756 -0.024708 -0.000047 0.990158
+          0.104854 -0.000225 0.000080
+          0.666667
+          0.115752 -0.013456 -0.072338 0.990549
+          0.104854 -0.000225 0.000079
+          0.700000
+          0.050286 0.106235 -0.086949 0.989255
+          0.104854 -0.000225 0.000080
+          0.733334
+          -0.028365 0.201499 -0.081532 0.975677
+          0.104854 -0.000225 0.000080
+          0.766667
+          -0.172481 0.252802 -0.088807 0.947869
+          0.104854 -0.000225 0.000080
+          0.800000
+          -0.373750 0.214710 -0.085827 0.898245
+          0.104854 -0.000225 0.000080
+          0.833334
+          -0.554003 0.092593 -0.053071 0.825645
+          0.104854 -0.000226 0.000080
+          0.866667
+          -0.675154 -0.000200 -0.049149 0.736038
+          0.104854 -0.000225 0.000081
+          0.900000
+          -0.691765 -0.012895 -0.057452 0.719718
+          0.104854 -0.000225 0.000079
+          0.933334
+          -0.635591 0.033506 -0.078953 0.767247
+          0.104854 -0.000225 0.000080
+          0.966667
+          -0.559880 0.096518 -0.109249 0.815649
+          0.104854 -0.000224 0.000080
+          1.000000
+          -0.482212 0.103838 -0.132230 0.859770
+          0.104854 -0.000225 0.000081
+          1.033334
+          -0.414794 0.060834 -0.122488 0.899579
+          0.104854 -0.000225 0.000079
+          1.066667
+          -0.305839 0.026125 -0.070255 0.949128
+          0.104854 -0.000226 0.000081
+          1.100000
+          -0.146067 0.047099 0.023500 0.987873
+          0.104854 -0.000226 0.000080
+          1.133334
+          0.030787 0.042692 0.058621 0.996892
+          0.104854 -0.000224 0.000081
+          1.166667
+          0.137756 -0.025197 -0.000047 0.990146
+          0.104854 -0.000224 0.000080
+          1.200000
+          0.115752 -0.013456 -0.072338 0.990549
+          0.104854 -0.000225 0.000081
+          1.233333
+          0.049797 0.106234 -0.086949 0.989280
+          0.104854 -0.000225 0.000080
+          1.266667
+          -0.028853 0.201498 -0.081043 0.975704
+          0.104854 -0.000225 0.000080
+          1.300000
+          -0.172480 0.252802 -0.088807 0.947869
+          0.104854 -0.000225 0.000080
+          1.333333
+          -0.373748 0.214711 -0.085827 0.898246
+          0.104854 -0.000226 0.000079
+          1.366667
+          -0.554002 0.093083 -0.053072 0.825591
+          0.104854 -0.000226 0.000081
+          1.400000
+          -0.675153 -0.000200 -0.049149 0.736039
+          0.104854 -0.000226 0.000080
+          1.433333
+          -0.691765 -0.012407 -0.057940 0.719687
+          0.104854 -0.000226 0.000079
+          1.466667
+          -0.635592 0.033994 -0.078952 0.767224
+          0.104854 -0.000226 0.000081
+          1.500000
+          -0.559881 0.097494 -0.109248 0.815532
+          0.104854 -0.000225 0.000079
+          1.533333
+          -0.481725 0.104815 -0.132230 0.859925
+          0.104854 -0.000225 0.000079
+          1.566666
+          -0.414795 0.060835 -0.122488 0.899578
+          0.104854 -0.000224 0.000081
+          1.600000
+      "L_Leg_sjnt_1"
+      49
+          0.629310 0.012204 0.010389 0.776989
+          0.000000 -0.461808 0.000000
+          0.000000
+          0.605132 0.006346 0.005519 0.796081
+          0.000000 -0.461807 0.000000
+          0.033333
+          0.478877 -0.001709 -0.000621 0.877880
+          0.000000 -0.461808 0.000000
+          0.066667
+          0.249328 -0.003052 -0.001116 0.968414
+          0.000000 -0.461808 0.000000
+          0.100000
+          0.125031 0.000671 0.000108 0.992153
+          0.000000 -0.461808 0.000000
+          0.133333
+          0.322102 0.000518 0.001712 0.946703
+          0.000000 -0.461808 0.000000
+          0.166667
+          0.595362 -0.001344 0.003081 0.803451
+          0.000000 -0.461807 0.000000
+          0.200000
+          0.797559 -0.004151 0.001316 0.603225
+          0.000000 -0.461807 -0.000000
+          0.233333
+          0.908185 -0.001284 0.007296 0.418503
+          0.000000 -0.461808 0.000000
+          0.266667
+          0.936026 0.000760 0.011675 0.351735
+          0.000000 -0.461808 0.000000
+          0.300000
+          0.899392 0.000517 0.004372 0.437120
+          0.000000 -0.461808 0.000000
+          0.333333
+          0.779976 0.000793 0.000137 0.625809
+          0.000000 -0.461808 0.000000
+          0.366667
+          0.599026 0.005858 0.003081 0.800703
+          0.000000 -0.461808 0.000000
+          0.400000
+          0.482055 0.012205 0.006323 0.876033
+          0.000000 -0.461808 0.000000
+          0.433333
+          0.506232 0.011717 0.007530 0.862285
+          0.000000 -0.461807 0.000000
+          0.466667
+          0.583644 0.011228 0.008950 0.811883
+          0.000000 -0.461807 0.000000
+          0.500000
+          0.629310 0.012204 0.010389 0.776989
+          0.000000 -0.461808 0.000000
+          0.533333
+          0.605132 0.006346 0.005519 0.796081
+          0.000000 -0.461807 0.000000
+          0.566667
+          0.478876 -0.001709 -0.000621 0.877881
+          0.000000 -0.461807 -0.000000
+          0.600000
+          0.249328 -0.003052 -0.001116 0.968414
+          0.000000 -0.461808 0.000000
+          0.633333
+          0.124788 0.000671 0.000108 0.992183
+          0.000000 -0.461807 0.000000
+          0.666667
+          0.322103 0.000518 0.001712 0.946703
+          0.000000 -0.461807 0.000000
+          0.700000
+          0.595607 -0.001283 0.003081 0.803269
+          0.000000 -0.461808 0.000000
+          0.733334
+          0.797560 -0.004151 0.001378 0.603224
+          0.000000 -0.461807 -0.000000
+          0.766667
+          0.908185 -0.001223 0.007296 0.418503
+          0.000000 -0.461807 0.000000
+          0.800000
+          0.936026 0.000760 0.012163 0.351719
+          0.000000 -0.461807 -0.000000
+          0.833334
+          0.899392 0.000517 0.004372 0.437122
+          0.000000 -0.461807 -0.000000
+          0.866667
+          0.780219 0.000793 0.000137 0.625506
+          0.000000 -0.461807 0.000000
+          0.900000
+          0.599024 0.005858 0.003081 0.800704
+          0.000000 -0.461808 0.000000
+          0.933334
+          0.482056 0.012205 0.006323 0.876033
+          0.000000 -0.461809 0.000000
+          0.966667
+          0.506233 0.011717 0.007530 0.862284
+          0.000000 -0.461807 0.000000
+          1.000000
+          0.583645 0.011228 0.008950 0.811882
+          0.000000 -0.461809 0.000000
+          1.033334
+          0.629310 0.012204 0.010389 0.776989
+          0.000000 -0.461808 -0.000000
+          1.066667
+          0.605131 0.006346 0.005519 0.796082
+          0.000000 -0.461808 0.000000
+          1.100000
+          0.478876 -0.001709 -0.000621 0.877881
+          0.000000 -0.461807 0.000001
+          1.133334
+          0.249328 -0.003052 -0.001116 0.968414
+          0.000000 -0.461808 0.000000
+          1.166667
+          0.124788 0.000671 0.000108 0.992183
+          0.000000 -0.461807 0.000000
+          1.200000
+          0.322102 0.000518 0.001712 0.946703
+          0.000000 -0.461808 0.000000
+          1.233333
+          0.595607 -0.001283 0.003081 0.803269
+          -0.000000 -0.461807 0.000000
+          1.266667
+          0.797559 -0.004151 0.001377 0.603225
+          0.000000 -0.461807 0.000001
+          1.300000
+          0.908185 -0.001223 0.007296 0.418503
+          0.000000 -0.461807 0.000000
+          1.333333
+          0.936026 0.000760 0.011675 0.351735
+          0.000000 -0.461806 0.000000
+          1.366667
+          0.899393 0.000517 0.004372 0.437120
+          -0.000000 -0.461809 0.000001
+          1.400000
+          0.779977 0.000793 0.000137 0.625808
+          -0.000000 -0.461805 0.000000
+          1.433333
+          0.599026 0.005858 0.003081 0.800702
+          0.000000 -0.461804 -0.000000
+          1.466667
+          0.482300 0.012205 0.006323 0.875898
+          0.000000 -0.461813 0.000002
+          1.500000
+          0.506476 0.011717 0.007530 0.862142
+          0.000000 -0.461807 0.000000
+          1.533333
+          0.583888 0.011228 0.008950 0.811707
+          0.000000 -0.461808 0.000001
+          1.566666
+          0.629310 0.012204 0.010389 0.776989
+          0.000000 -0.461807 -0.000001
+          1.600000
+      "L_Foot_sjnt_0"
+      49
+          -0.726425 -0.034266 -0.036536 0.685418
+          -0.000001 -0.388173 -0.000000
+          0.000000
+          -0.711790 -0.060139 -0.103476 0.692121
+          -0.000001 -0.388173 -0.000000
+          0.033333
+          -0.678092 -0.081612 -0.144048 0.716087
+          -0.000001 -0.388173 -0.000000
+          0.066667
+          -0.509073 -0.008760 -0.123620 0.851754
+          -0.000001 -0.388173 -0.000000
+          0.100000
+          -0.267239 0.028436 -0.060231 0.961326
+          -0.000001 -0.388173 -0.000000
+          0.133333
+          -0.303902 -0.012594 -0.146199 0.941334
+          -0.000001 -0.388173 -0.000000
+          0.166667
+          -0.354697 -0.051205 -0.143251 0.922522
+          -0.000001 -0.388173 -0.000000
+          0.200000
+          -0.506140 0.067439 -0.068403 0.857085
+          -0.000001 -0.388173 -0.000000
+          0.233333
+          -0.631652 0.053695 0.018624 0.773166
+          -0.000001 -0.388173 -0.000000
+          0.266667
+          -0.723982 0.068309 0.031877 0.685688
+          -0.000001 -0.388173 -0.000000
+          0.300000
+          -0.729353 0.062444 0.034322 0.680416
+          -0.000001 -0.388173 -0.000000
+          0.333333
+          -0.674168 0.081038 0.000093 0.734118
+          -0.000001 -0.388173 -0.000000
+          0.366667
+          -0.603834 0.031252 -0.064935 0.793846
+          -0.000001 -0.388173 -0.000000
+          0.400000
+          -0.567693 0.016128 -0.100132 0.816969
+          -0.000001 -0.388172 -0.000001
+          0.433333
+          -0.596499 0.004875 -0.065922 0.799887
+          -0.000001 -0.388172 0.000000
+          0.466667
+          -0.695144 -0.028888 0.004479 0.718276
+          -0.000001 -0.388173 -0.000001
+          0.500000
+          -0.726425 -0.034266 -0.036536 0.685418
+          -0.000001 -0.388172 -0.000000
+          0.533333
+          -0.711790 -0.060139 -0.103476 0.692121
+          -0.000001 -0.388173 -0.000000
+          0.566667
+          -0.678092 -0.081612 -0.144048 0.716087
+          -0.000001 -0.388173 -0.000000
+          0.600000
+          -0.509073 -0.008760 -0.123619 0.851755
+          -0.000001 -0.388173 -0.000000
+          0.633333
+          -0.267239 0.028436 -0.060232 0.961326
+          -0.000001 -0.388173 -0.000000
+          0.666667
+          -0.303902 -0.012595 -0.146199 0.941334
+          -0.000001 -0.388172 -0.000000
+          0.700000
+          -0.354698 -0.051204 -0.143251 0.922522
+          -0.000001 -0.388173 -0.000000
+          0.733334
+          -0.506141 0.067439 -0.068403 0.857085
+          -0.000001 -0.388173 -0.000000
+          0.766667
+          -0.631653 0.053695 0.018624 0.773166
+          -0.000001 -0.388173 -0.000000
+          0.800000
+          -0.723982 0.068309 0.031878 0.685688
+          -0.000001 -0.388173 -0.000000
+          0.833334
+          -0.729353 0.062444 0.034322 0.680417
+          -0.000001 -0.388173 -0.000000
+          0.866667
+          -0.674168 0.081038 0.000093 0.734119
+          -0.000001 -0.388173 -0.000000
+          0.900000
+          -0.603833 0.031251 -0.064935 0.793846
+          -0.000001 -0.388174 0.000000
+          0.933334
+          -0.567694 0.016128 -0.100132 0.816968
+          -0.000001 -0.388173 0.000000
+          0.966667
+          -0.596500 0.004874 -0.065921 0.799886
+          -0.000001 -0.388173 -0.000000
+          1.000000
+          -0.695145 -0.028888 0.004479 0.718275
+          -0.000001 -0.388173 0.000000
+          1.033334
+          -0.726425 -0.034267 -0.036537 0.685418
+          -0.000001 -0.388172 -0.000001
+          1.066667
+          -0.711789 -0.060139 -0.103477 0.692121
+          -0.000001 -0.388174 -0.000000
+          1.100000
+          -0.678092 -0.081612 -0.144048 0.716087
+          -0.000001 -0.388173 -0.000001
+          1.133334
+          -0.509073 -0.008760 -0.123619 0.851755
+          -0.000001 -0.388172 -0.000000
+          1.166667
+          -0.267239 0.028436 -0.060232 0.961326
+          -0.000001 -0.388173 -0.000001
+          1.200000
+          -0.303902 -0.012594 -0.146199 0.941335
+          -0.000001 -0.388174 -0.000000
+          1.233333
+          -0.354697 -0.051205 -0.143251 0.922522
+          -0.000001 -0.388173 -0.000000
+          1.266667
+          -0.506140 0.067439 -0.068403 0.857085
+          -0.000001 -0.388173 0.000000
+          1.300000
+          -0.631652 0.053695 0.018624 0.773166
+          -0.000001 -0.388173 -0.000000
+          1.333333
+          -0.723982 0.068309 0.031877 0.685688
+          -0.000001 -0.388173 -0.000000
+          1.366667
+          -0.729353 0.062444 0.034322 0.680416
+          -0.000001 -0.388173 -0.000000
+          1.400000
+          -0.674169 0.081038 0.000093 0.734118
+          -0.000001 -0.388172 0.000000
+          1.433333
+          -0.603834 0.031252 -0.064935 0.793846
+          -0.000001 -0.388173 -0.000001
+          1.466667
+          -0.567693 0.016128 -0.100132 0.816969
+          -0.000001 -0.388172 -0.000001
+          1.500000
+          -0.596499 0.004875 -0.065922 0.799887
+          -0.000001 -0.388173 0.000000
+          1.533333
+          -0.695144 -0.028887 0.004479 0.718276
+          -0.000001 -0.388173 0.000000
+          1.566666
+          -0.726425 -0.034266 -0.036536 0.685418
+          -0.000001 -0.388172 -0.000001
+          1.600000
+      "C_Spine_sjnt_0"
+      49
+          0.182129 -0.000897 0.003998 0.983266
+          0.000000 0.055442 0.000000
+          0.000000
+          0.182861 -0.018556 0.006771 0.982940
+          0.000000 0.055442 -0.000000
+          0.033333
+          0.176758 -0.034915 0.014397 0.983530
+          0.000000 0.055442 0.000000
+          0.066667
+          0.166016 -0.044195 0.023794 0.984845
+          -0.000000 0.055442 0.000000
+          0.100000
+          0.162353 -0.041510 0.027701 0.985470
+          -0.000000 0.055442 -0.000000
+          0.133333
+          0.169678 -0.035651 0.028435 0.984444
+          0.000000 0.055442 0.000000
+          0.166667
+          0.180664 -0.028815 0.027826 0.982729
+          -0.000000 0.055442 0.000000
+          0.200000
+          0.187500 -0.021002 0.028804 0.981618
+          -0.000000 0.055442 0.000000
+          0.233333
+          0.190430 -0.008307 0.026854 0.981298
+          -0.000000 0.055442 -0.000000
+          0.266667
+          0.195557 0.007046 0.018190 0.980498
+          0.000000 0.055442 0.000000
+          0.300000
+          0.199219 0.018186 0.011234 0.979722
+          -0.000000 0.055442 0.000000
+          0.333333
+          0.189209 0.029541 -0.001153 0.981492
+          0.000000 0.055442 -0.000000
+          0.366667
+          0.186523 0.029908 -0.003869 0.981988
+          0.000000 0.055442 0.000000
+          0.400000
+          0.195801 0.023070 0.004735 0.980361
+          0.000000 0.055442 0.000000
+          0.433333
+          0.202637 0.011534 0.008120 0.979152
+          0.000000 0.055442 0.000000
+          0.466667
+          0.204590 0.004057 0.007539 0.978810
+          0.000000 0.055442 0.000000
+          0.500000
+          0.204590 -0.006410 0.006560 0.978805
+          -0.000000 0.055442 0.000000
+          0.533333
+          0.206299 -0.023928 0.009944 0.978146
+          0.000000 0.055442 0.000000
+          0.566667
+          0.201660 -0.039799 0.018302 0.978476
+          0.000000 0.055442 0.000000
+          0.600000
+          0.192139 -0.048590 0.028310 0.979755
+          0.000000 0.055442 0.000000
+          0.633333
+          0.189453 -0.045173 0.032461 0.980313
+          0.000000 0.055442 0.000000
+          0.666667
+          0.197021 -0.038581 0.033195 0.979077
+          0.000000 0.055441 -0.000000
+          0.700000
+          0.208008 -0.031013 0.032464 0.977096
+          0.000000 0.055442 0.000000
+          0.733334
+          0.214600 -0.022346 0.033442 0.975874
+          0.000000 0.055442 0.000000
+          0.766667
+          0.217041 -0.008674 0.031248 0.975624
+          0.000000 0.055442 0.000000
+          0.800000
+          0.221680 0.007442 0.022340 0.974835
+          -0.000000 0.055442 -0.000000
+          0.833334
+          0.224609 0.019406 0.015202 0.974137
+          0.000000 0.055442 0.000000
+          0.866667
+          0.214355 0.031249 0.002662 0.976252
+          -0.000000 0.055442 0.000000
+          0.900000
+          0.211670 0.032226 0.000066 0.976810
+          -0.000000 0.055442 -0.000001
+          0.933334
+          0.220703 0.026365 0.009039 0.974943
+          0.000000 0.055441 0.000000
+          0.966667
+          0.228027 0.015256 0.012882 0.973450
+          0.000000 0.055442 -0.000000
+          1.000000
+          0.229980 0.007993 0.012575 0.973081
+          0.000000 0.055443 0.000000
+          1.033334
+          0.229004 -0.002352 0.011779 0.973351
+          0.000000 0.055442 0.000000
+          1.066667
+          0.228760 -0.019657 0.015316 0.973164
+          -0.000000 0.055442 0.000000
+          1.100000
+          0.221436 -0.035405 0.023430 0.974250
+          0.000000 0.055442 -0.000001
+          1.133334
+          0.207764 -0.043952 0.032705 0.976644
+          -0.000000 0.055443 0.000000
+          1.166667
+          0.198730 -0.040779 0.035392 0.978566
+          -0.000000 0.055442 -0.000001
+          1.200000
+          0.198975 -0.034431 0.034416 0.978795
+          0.000000 0.055442 0.000000
+          1.233333
+          0.201904 -0.027106 0.032221 0.978500
+          0.000000 0.055442 -0.000000
+          1.266667
+          0.199951 -0.018806 0.031490 0.979119
+          0.000000 0.055442 -0.000001
+          1.300000
+          0.194092 -0.005591 0.028075 0.980566
+          -0.000000 0.055443 -0.000000
+          1.333333
+          0.190918 0.010433 0.018191 0.981382
+          0.000000 0.055442 -0.000001
+          1.366667
+          0.187256 0.022337 0.010442 0.982002
+          0.000000 0.055441 0.000000
+          1.400000
+          0.172119 0.034180 -0.002419 0.984480
+          0.000000 0.055442 0.000000
+          1.433333
+          0.166504 0.035157 -0.005425 0.985399
+          0.000000 0.055442 -0.000001
+          1.466667
+          0.174560 0.028686 0.003027 0.984224
+          0.000000 0.055441 0.000001
+          1.500000
+          0.181152 0.017089 0.006168 0.983287
+          0.000000 0.055443 -0.000001
+          1.533333
+          0.182861 0.009642 0.005373 0.983077
+          0.000000 0.055442 0.000000
+          1.566666
+          0.182129 -0.000897 0.003998 0.983266
+          0.000000 0.055443 0.000001
+          1.600000
+      "C_Spine_sjnt_1"
+      49
+          0.069092 -0.013238 -0.032718 0.996986
+          0.000000 0.049000 -0.000000
+          0.000000
+          0.064697 -0.030756 -0.025153 0.997114
+          -0.000000 0.049000 0.000000
+          0.033333
+          0.054321 -0.032469 -0.008674 0.997958
+          -0.000000 0.049000 0.000000
+          0.066667
+          0.038086 -0.029422 0.014459 0.998737
+          0.000000 0.049000 0.000000
+          0.100000
+          0.029907 -0.027838 0.027826 0.998778
+          0.000000 0.049000 -0.000000
+          0.133333
+          0.035278 -0.026984 0.028193 0.998615
+          0.000000 0.049000 0.000000
+          0.166667
+          0.045776 -0.023321 0.026118 0.998338
+          0.000000 0.049000 0.000000
+          0.200000
+          0.051514 -0.012945 0.026364 0.998240
+          0.000000 0.049000 0.000000
+          0.233333
+          0.049194 0.002604 0.023316 0.998514
+          -0.000000 0.049000 0.000000
+          0.266667
+          0.048462 0.018796 0.012150 0.998574
+          0.000000 0.049000 -0.000000
+          0.300000
+          0.046265 0.023437 0.000124 0.998654
+          -0.000000 0.049000 -0.000000
+          0.333333
+          0.026306 0.026128 -0.020991 0.999092
+          -0.000000 0.049000 -0.000000
+          0.366667
+          0.018677 0.023810 -0.031489 0.999046
+          0.000000 0.049000 0.000000
+          0.400000
+          0.030273 0.021368 -0.026729 0.998956
+          -0.000000 0.049000 0.000000
+          0.433333
+          0.043701 0.014594 -0.030026 0.998487
+          -0.000000 0.049000 0.000000
+          0.466667
+          0.049805 -0.001351 -0.034424 0.998165
+          0.000000 0.049000 0.000000
+          0.500000
+          0.046509 -0.019890 -0.032963 0.998176
+          0.000000 0.049000 -0.000000
+          0.533333
+          0.043457 -0.037348 -0.026375 0.998009
+          0.000000 0.049000 0.000000
+          0.566667
+          0.034058 -0.039549 -0.009774 0.998589
+          -0.000000 0.049000 0.000000
+          0.600000
+          0.020508 -0.036136 0.013664 0.999043
+          0.000000 0.049000 0.000000
+          0.633333
+          0.015442 -0.033697 0.027459 0.998936
+          -0.000000 0.049000 0.000000
+          0.666667
+          0.024170 -0.031256 0.027703 0.998835
+          0.000000 0.049000 -0.000000
+          0.700000
+          0.038330 -0.025396 0.025263 0.998623
+          0.000000 0.049000 0.000000
+          0.733334
+          0.047974 -0.012762 0.024778 0.998460
+          0.000000 0.049000 0.000000
+          0.766667
+          0.049683 0.003871 0.020753 0.998542
+          -0.000000 0.049000 -0.000000
+          0.800000
+          0.053223 0.018919 0.008854 0.998364
+          0.000000 0.049000 0.000000
+          0.833334
+          0.054443 0.024171 -0.004023 0.998216
+          0.000000 0.049000 -0.000000
+          0.866667
+          0.038208 0.026983 -0.026239 0.998561
+          0.000000 0.049001 0.000000
+          0.900000
+          0.034058 0.024910 -0.037104 0.998420
+          0.000000 0.049000 0.000000
+          0.933334
+          0.048218 0.022956 -0.031978 0.998061
+          -0.000000 0.049000 0.000000
+          0.966667
+          0.063721 0.016242 -0.033932 0.997259
+          -0.000000 0.049000 0.000000
+          1.000000
+          0.071045 0.000049 -0.036621 0.996801
+          0.000000 0.049000 -0.000000
+          1.033334
+          0.068604 -0.017937 -0.033695 0.996913
+          0.000000 0.049000 0.000000
+          1.066667
+          0.065430 -0.033930 -0.025032 0.996966
+          -0.000000 0.049000 0.000001
+          1.100000
+          0.055298 -0.034666 -0.008125 0.997835
+          -0.000000 0.049000 0.000000
+          1.133334
+          0.040649 -0.029422 0.014886 0.998629
+          0.000000 0.049000 0.000000
+          1.166667
+          0.034546 -0.026373 0.028315 0.998654
+          0.000000 0.049000 -0.000000
+          1.200000
+          0.041992 -0.024420 0.028193 0.998422
+          0.000000 0.049001 0.000000
+          1.233333
+          0.054565 -0.019170 0.025264 0.998007
+          -0.000000 0.049001 0.000000
+          1.266667
+          0.062378 -0.007055 0.024657 0.997723
+          -0.000000 0.048999 0.000000
+          1.300000
+          0.062134 0.009090 0.021120 0.997803
+          0.000000 0.049000 -0.000000
+          1.333333
+          0.063721 0.023923 0.010625 0.997624
+          0.000000 0.049000 0.000000
+          1.366667
+          0.063232 0.029175 -0.001192 0.997572
+          0.000000 0.049000 0.000000
+          1.400000
+          0.045654 0.032476 -0.022576 0.998174
+          -0.000000 0.049000 -0.000000
+          1.433333
+          0.039917 0.030525 -0.032952 0.998193
+          -0.000000 0.049000 0.000000
+          1.466667
+          0.052246 0.027838 -0.027094 0.997878
+          0.000000 0.049000 0.000000
+          1.500000
+          0.065918 0.020026 -0.028560 0.997215
+          0.000000 0.049000 0.000000
+          1.533333
+          0.072266 0.003623 -0.032470 0.996850
+          0.000000 0.049000 0.000000
+          1.566666
+          0.069092 -0.013238 -0.032718 0.996986
+          0.000000 0.048999 -0.000000
+          1.600000
+      "C_Spine_sjnt_2"
+      49
+          0.035789 -0.015533 -0.028747 0.998825
+          0.000000 0.049000 0.000000
+          0.000000
+          0.027380 -0.032538 -0.016494 0.998959
+          0.000000 0.049000 0.000000
+          0.033333
+          0.019393 -0.033672 -0.003008 0.999240
+          0.000000 0.049000 0.000000
+          0.066667
+          0.011588 -0.029728 0.016004 0.999363
+          0.000000 0.049000 -0.000000
+          0.100000
+          0.006875 -0.027882 0.027094 0.999220
+          -0.000000 0.049000 -0.000000
+          0.133333
+          0.007410 -0.026669 0.026110 0.999276
+          -0.000000 0.049000 -0.000000
+          0.166667
+          0.011427 -0.022740 0.022529 0.999422
+          0.000000 0.049000 0.000000
+          0.200000
+          0.014521 -0.012320 0.019318 0.999632
+          -0.000000 0.049000 0.000000
+          0.233333
+          0.011843 0.003017 0.013422 0.999835
+          0.000000 0.049000 0.000000
+          0.266667
+          0.007436 0.018759 0.003603 0.999790
+          -0.000000 0.049000 0.000000
+          0.300000
+          0.003769 0.022557 -0.006834 0.999715
+          -0.000000 0.049000 0.000000
+          0.333333
+          -0.007571 0.024687 -0.021751 0.999430
+          0.000000 0.049000 0.000000
+          0.366667
+          -0.013310 0.022334 -0.028844 0.999246
+          0.000000 0.049000 0.000000
+          0.400000
+          -0.008725 0.019645 -0.026991 0.999405
+          0.000000 0.049000 0.000000
+          0.433333
+          -0.000692 0.012255 -0.029452 0.999491
+          0.000000 0.049000 0.000000
+          0.466667
+          0.004826 -0.003827 -0.031849 0.999474
+          0.000000 0.049000 0.000000
+          0.500000
+          0.001234 -0.022318 -0.027846 0.999362
+          0.000000 0.049000 -0.000000
+          0.533333
+          -0.005082 -0.039301 -0.017376 0.999063
+          0.000000 0.049000 -0.000000
+          0.566667
+          -0.011402 -0.040963 -0.003415 0.999090
+          -0.000000 0.049000 0.000000
+          0.600000
+          -0.015505 -0.036334 0.016210 0.999088
+          0.000000 0.049000 0.000000
+          0.633333
+          -0.015557 -0.033536 0.027460 0.998939
+          0.000000 0.049000 -0.000000
+          0.666667
+          -0.009926 -0.030845 0.025914 0.999139
+          0.000000 0.049000 -0.000000
+          0.700000
+          -0.000420 -0.024700 0.021567 0.999462
+          0.000000 0.049000 -0.000000
+          0.733334
+          0.009048 -0.012053 0.016839 0.999745
+          0.000000 0.049000 0.000000
+          0.766667
+          0.012691 0.004171 0.009291 0.999868
+          -0.000000 0.049000 -0.000000
+          0.800000
+          0.014570 0.018595 -0.001705 0.999720
+          0.000000 0.049000 -0.000000
+          0.833334
+          0.016473 0.023148 -0.013912 0.999500
+          -0.000000 0.048999 -0.000000
+          0.866667
+          0.010874 0.025503 -0.030836 0.999140
+          0.000000 0.049000 0.000000
+          0.900000
+          0.010532 0.023126 -0.038700 0.998928
+          0.000000 0.049000 0.000000
+          0.933334
+          0.018890 0.020707 -0.036848 0.998928
+          0.000000 0.049000 -0.000000
+          0.966667
+          0.029662 0.013677 -0.038215 0.998736
+          0.000000 0.049000 0.000000
+          1.000000
+          0.037441 -0.002451 -0.037596 0.998588
+          -0.000000 0.049000 0.000000
+          1.033334
+          0.035051 -0.020153 -0.029876 0.998736
+          -0.000000 0.049000 0.000000
+          1.066667
+          0.028429 -0.035475 -0.016134 0.998836
+          0.000000 0.049000 0.000000
+          1.100000
+          0.020660 -0.035635 -0.002323 0.999149
+          0.000000 0.049000 0.000000
+          1.133334
+          0.015441 -0.029481 0.016084 0.999317
+          0.000000 0.049000 0.000000
+          1.166667
+          0.013987 -0.026406 0.026998 0.999189
+          0.000000 0.049000 0.000000
+          1.200000
+          0.017652 -0.024205 0.025464 0.999227
+          -0.000000 0.049000 0.000000
+          1.233333
+          0.024932 -0.018542 0.020576 0.999305
+          -0.000000 0.049001 -0.000000
+          1.266667
+          0.031402 -0.006744 0.015583 0.999363
+          -0.000000 0.049000 -0.000000
+          1.300000
+          0.032080 0.008981 0.008646 0.999408
+          0.000000 0.049000 0.000000
+          1.333333
+          0.030832 0.023242 -0.000592 0.999254
+          -0.000000 0.049000 -0.000000
+          1.366667
+          0.030322 0.028004 -0.011101 0.999086
+          0.000000 0.049001 -0.000001
+          1.400000
+          0.022413 0.030805 -0.026099 0.998933
+          0.000000 0.049001 0.000000
+          1.433333
+          0.019415 0.028668 -0.033409 0.998842
+          0.000000 0.049000 0.000000
+          1.466667
+          0.025160 0.025750 -0.030545 0.998885
+          0.000000 0.048999 -0.000001
+          1.500000
+          0.033407 0.017719 -0.030660 0.998814
+          0.000000 0.048999 0.000000
+          1.533333
+          0.039390 0.001272 -0.031482 0.998727
+          0.000000 0.048999 0.000000
+          1.566666
+          0.035787 -0.015533 -0.028748 0.998825
+          0.000000 0.049001 0.000000
+          1.600000
+      "C_Spine_sjnt_4"
+      49
+          0.015028 -0.033854 -0.028118 0.998918
+          0.000000 0.098001 0.000000
+          0.000000
+          0.003492 -0.066901 -0.006297 0.997734
+          0.000000 0.098001 -0.000000
+          0.033333
+          -0.002257 -0.068253 0.010302 0.997612
+          0.000000 0.098001 0.000000
+          0.066667
+          0.001837 -0.060032 0.029695 0.997753
+          -0.000000 0.098001 0.000000
+          0.100000
+          0.002944 -0.056257 0.038646 0.997664
+          0.000000 0.098001 0.000000
+          0.133333
+          0.000202 -0.053353 0.036380 0.997913
+          0.000000 0.098001 0.000000
+          0.166667
+          0.000486 -0.045610 0.030206 0.998502
+          -0.000000 0.098001 0.000000
+          0.200000
+          0.002928 -0.025194 0.021368 0.999450
+          0.000000 0.098001 0.000000
+          0.233333
+          0.000399 0.005013 0.010756 0.999930
+          0.000000 0.098001 0.000000
+          0.266667
+          -0.010889 0.036126 -0.000532 0.999288
+          0.000000 0.098001 0.000000
+          0.300000
+          -0.012050 0.043806 -0.013999 0.998869
+          0.000000 0.098001 0.000000
+          0.333333
+          -0.010475 0.048394 -0.017766 0.998615
+          0.000000 0.098001 0.000000
+          0.366667
+          -0.012805 0.043506 -0.019075 0.998789
+          0.000000 0.098001 -0.000000
+          0.400000
+          -0.015585 0.037295 -0.027623 0.998801
+          0.000000 0.098001 0.000000
+          0.433333
+          -0.015651 0.022397 -0.031386 0.999134
+          0.000000 0.098001 0.000000
+          0.466667
+          -0.013967 -0.010011 -0.033876 0.999278
+          0.000000 0.098001 -0.000000
+          0.500000
+          -0.019973 -0.046828 -0.025332 0.998382
+          -0.000000 0.098001 0.000000
+          0.533333
+          -0.029651 -0.080990 -0.005561 0.996258
+          -0.000000 0.098001 0.000000
+          0.566667
+          -0.033232 -0.082759 0.012109 0.995942
+          0.000000 0.098001 -0.000000
+          0.600000
+          -0.025482 -0.073288 0.032287 0.996462
+          0.000000 0.098001 -0.000000
+          0.633333
+          -0.019845 -0.067832 0.040707 0.996668
+          0.000000 0.098001 -0.000000
+          0.666667
+          -0.017529 -0.061560 0.037250 0.997254
+          0.000000 0.098001 -0.000000
+          0.700000
+          -0.011507 -0.048985 0.029899 0.998286
+          0.000000 0.098001 -0.000000
+          0.733334
+          -0.002799 -0.024470 0.019385 0.999509
+          -0.000000 0.098001 0.000000
+          0.766667
+          0.001132 0.007356 0.006704 0.999950
+          0.000000 0.098001 -0.000000
+          0.800000
+          -0.003562 0.035840 -0.006135 0.999332
+          0.000000 0.098001 0.000000
+          0.833334
+          0.000993 0.044246 -0.021856 0.998781
+          0.000000 0.098001 0.000000
+          0.866667
+          0.008845 0.048756 -0.027698 0.998387
+          0.000000 0.098001 0.000000
+          0.900000
+          0.011756 0.044800 -0.029892 0.998479
+          0.000000 0.098001 -0.000000
+          0.933334
+          0.012568 0.038673 -0.039049 0.998410
+          -0.000000 0.098001 0.000000
+          0.966667
+          0.014846 0.023709 -0.042502 0.998705
+          0.000000 0.098001 -0.000000
+          1.000000
+          0.018669 -0.007964 -0.041902 0.998916
+          0.000000 0.098001 0.000000
+          1.033334
+          0.014269 -0.042404 -0.029134 0.998574
+          0.000000 0.098001 0.000000
+          1.066667
+          0.004286 -0.072749 -0.006038 0.997323
+          0.000000 0.098001 0.000000
+          1.100000
+          -0.000991 -0.071178 0.010768 0.997405
+          0.000000 0.098001 0.000000
+          1.133334
+          0.005574 -0.059566 0.029115 0.997784
+          0.000000 0.098001 0.000000
+          1.166667
+          0.009700 -0.053372 0.037695 0.997816
+          0.000000 0.098001 0.000000
+          1.200000
+          0.010218 -0.048531 0.034826 0.998162
+          0.000000 0.098001 0.000000
+          1.233333
+          0.014121 -0.037874 0.027411 0.998807
+          0.000000 0.098001 0.000000
+          1.266667
+          0.019937 -0.014959 0.017028 0.999544
+          -0.000000 0.098001 0.000000
+          1.300000
+          0.021373 0.015654 0.005036 0.999636
+          -0.000000 0.098001 0.000000
+          1.333333
+          0.013295 0.044138 -0.005928 0.998919
+          0.000000 0.098001 0.000000
+          1.366667
+          0.015420 0.053066 -0.019630 0.998279
+          0.000000 0.098001 0.000000
+          1.400000
+          0.020488 0.058795 -0.023303 0.997788
+          0.000000 0.098001 0.000000
+          1.433333
+          0.021038 0.055323 -0.025003 0.997934
+          0.000000 0.098001 0.000001
+          1.466667
+          0.019070 0.048255 -0.033121 0.998104
+          0.000000 0.098001 -0.000000
+          1.500000
+          0.019530 0.031706 -0.035550 0.998674
+          0.000000 0.098002 -0.000000
+          1.533333
+          0.021353 -0.000439 -0.035886 0.999128
+          -0.000000 0.098001 0.000000
+          1.566666
+          0.015030 -0.033854 -0.028117 0.998918
+          0.000000 0.098001 0.000000
+          1.600000
+      "L_Clavicle_sjnt_0"
+      49
+          0.764010 -0.631175 -0.051299 0.123593
+          0.007299 0.208857 0.030677
+          0.000000
+          0.795554 -0.596008 -0.022972 0.106489
+          0.007299 0.208857 0.030677
+          0.033333
+          0.824675 -0.554484 -0.033462 0.106485
+          0.007299 0.208857 0.030677
+          0.066667
+          -0.834852 0.530540 0.064463 -0.131886
+          0.007299 0.208857 0.030677
+          0.100000
+          -0.839426 0.523212 0.066903 -0.130908
+          0.007299 0.208857 0.030677
+          0.133333
+          -0.838380 0.529077 0.054697 -0.119184
+          0.007299 0.208857 0.030677
+          0.166667
+          -0.833335 0.538360 0.049328 -0.115277
+          0.007299 0.208857 0.030677
+          0.200000
+          -0.818684 0.558387 0.052994 -0.123095
+          0.007299 0.208857 0.030677
+          0.233333
+          -0.802608 0.582326 0.045186 -0.121144
+          0.007299 0.208857 0.030677
+          0.266667
+          -0.793801 0.590628 0.054709 -0.134336
+          0.007299 0.208857 0.030677
+          0.300000
+          -0.802328 0.576459 0.061298 -0.142151
+          0.007299 0.208857 0.030677
+          0.333333
+          -0.831839 0.544714 0.028455 -0.102575
+          0.007299 0.208857 0.030677
+          0.366667
+          0.829182 -0.551068 -0.016128 0.092316
+          0.007299 0.208857 0.030677
+          0.400000
+          0.800467 -0.588190 -0.029684 0.111374
+          0.007299 0.208857 0.030677
+          0.433333
+          0.767741 -0.622379 -0.063016 0.138737
+          0.007299 0.208857 0.030677
+          0.466667
+          0.757913 -0.634592 -0.065460 0.136296
+          0.007299 0.208857 0.030677
+          0.500000
+          0.764078 -0.630687 -0.049102 0.126524
+          0.007299 0.208857 0.030677
+          0.533333
+          0.795456 -0.596497 -0.024803 0.104047
+          0.007299 0.208857 0.030677
+          0.566667
+          0.824718 -0.554484 -0.033950 0.105996
+          0.007299 0.208857 0.030677
+          0.600000
+          -0.834812 0.530540 0.063974 -0.132374
+          0.007299 0.208858 0.030677
+          0.633333
+          -0.839389 0.523212 0.066414 -0.131397
+          0.007299 0.208857 0.030677
+          0.666667
+          -0.838380 0.529077 0.054697 -0.119184
+          0.007299 0.208857 0.030677
+          0.700000
+          -0.833320 0.538360 0.049572 -0.115277
+          0.007299 0.208857 0.030678
+          0.733334
+          -0.818684 0.558387 0.052994 -0.123095
+          0.007299 0.208857 0.030677
+          0.766667
+          -0.802534 0.582326 0.045186 -0.121633
+          0.007299 0.208858 0.030677
+          0.800000
+          -0.793801 0.590628 0.054709 -0.134336
+          0.007299 0.208857 0.030677
+          0.833334
+          -0.802329 0.576459 0.061298 -0.142150
+          0.007299 0.208857 0.030677
+          0.866667
+          0.831838 -0.544714 -0.028455 0.102575
+          0.007299 0.208857 0.030677
+          0.900000
+          0.829182 -0.551068 -0.016128 0.092317
+          0.007299 0.208857 0.030677
+          0.933334
+          0.800466 -0.588191 -0.029685 0.111374
+          0.007299 0.208857 0.030677
+          0.966667
+          0.767741 -0.622379 -0.063016 0.138737
+          0.007299 0.208857 0.030677
+          1.000000
+          0.757913 -0.634592 -0.065460 0.136296
+          0.007299 0.208857 0.030677
+          1.033334
+          0.764078 -0.630687 -0.049102 0.126524
+          0.007299 0.208857 0.030677
+          1.066667
+          0.795457 -0.596496 -0.024804 0.104047
+          0.007299 0.208857 0.030677
+          1.100000
+          0.824719 -0.554483 -0.033950 0.105996
+          0.007299 0.208857 0.030677
+          1.133334
+          -0.834812 0.530540 0.063974 -0.132375
+          0.007299 0.208857 0.030677
+          1.166667
+          -0.839389 0.523212 0.066414 -0.131397
+          0.007299 0.208857 0.030677
+          1.200000
+          -0.838380 0.529077 0.054697 -0.119184
+          0.007299 0.208858 0.030677
+          1.233333
+          -0.833320 0.538360 0.049572 -0.115277
+          0.007299 0.208857 0.030677
+          1.266667
+          -0.818684 0.558387 0.052994 -0.123096
+          0.007299 0.208857 0.030677
+          1.300000
+          -0.802534 0.582325 0.045186 -0.121633
+          0.007299 0.208857 0.030677
+          1.333333
+          -0.793801 0.590628 0.054709 -0.134336
+          0.007299 0.208857 0.030677
+          1.366667
+          -0.802328 0.576459 0.061298 -0.142150
+          0.007299 0.208857 0.030677
+          1.400000
+          0.831890 -0.544714 -0.028700 0.102087
+          0.007299 0.208857 0.030677
+          1.433333
+          0.828904 -0.551556 -0.016494 0.091828
+          0.007299 0.208857 0.030677
+          1.466667
+          0.800771 -0.587702 -0.029318 0.111862
+          0.007299 0.208856 0.030676
+          1.500000
+          0.767574 -0.622379 -0.061795 0.140202
+          0.007299 0.208857 0.030678
+          1.533333
+          0.757913 -0.634592 -0.065460 0.136296
+          0.007299 0.208857 0.030677
+          1.566666
+          0.764010 -0.631175 -0.051300 0.123593
+          0.007299 0.208857 0.030677
+          1.600000
+      "L_Arm_sjnt_0"
+      49
+          0.307215 0.008955 0.607429 0.732509
+          0.070455 -0.150703 0.005820
+          0.000000
+          0.331157 0.164789 0.589233 0.718321
+          0.070455 -0.150704 0.005819
+          0.033333
+          0.314079 0.271773 0.536599 0.734545
+          0.070455 -0.150704 0.005819
+          0.066667
+          -0.268178 -0.341143 -0.487743 -0.757502
+          0.070455 -0.150703 0.005819
+          0.100000
+          -0.143618 -0.303047 -0.474765 -0.813717
+          0.070455 -0.150704 0.005820
+          0.133333
+          -0.054695 -0.160408 -0.522606 -0.835560
+          0.070455 -0.150704 0.005819
+          0.166667
+          -0.064922 0.017412 -0.599785 -0.797333
+          0.070455 -0.150704 0.005820
+          0.200000
+          -0.156743 0.205012 -0.625959 -0.735920
+          0.070455 -0.150704 0.005820
+          0.233333
+          -0.208028 0.379916 -0.611832 -0.661854
+          0.070455 -0.150704 0.005819
+          0.266667
+          -0.181636 0.535280 -0.593529 -0.572894
+          0.070456 -0.150703 0.005820
+          0.300000
+          -0.062436 0.627618 -0.570551 -0.525994
+          0.070456 -0.150703 0.005819
+          0.333333
+          0.059686 0.621752 -0.555007 -0.549390
+          0.070456 -0.150703 0.005819
+          0.366667
+          -0.128549 -0.559700 0.535932 0.618860
+          0.070456 -0.150703 0.005819
+          0.400000
+          -0.097272 -0.481527 0.537274 0.685571
+          0.070455 -0.150704 0.005820
+          0.433333
+          0.040492 -0.386258 0.560624 0.731346
+          0.070455 -0.150704 0.005819
+          0.466667
+          0.178740 -0.215271 0.592514 0.755406
+          0.070455 -0.150703 0.005819
+          0.500000
+          0.269117 -0.020841 0.596798 0.755629
+          0.070455 -0.150704 0.005819
+          0.533333
+          0.271557 0.116431 0.603502 0.740599
+          0.070455 -0.150703 0.005819
+          0.566667
+          0.268154 0.224392 0.559784 0.751255
+          0.070455 -0.150703 0.005820
+          0.600000
+          -0.241798 -0.304996 -0.494696 -0.777037
+          0.070455 -0.150703 0.005819
+          0.633333
+          -0.119196 -0.273738 -0.469509 -0.830916
+          0.070456 -0.150703 0.005820
+          0.666667
+          -0.031736 -0.140868 -0.519427 -0.842226
+          0.070455 -0.150704 0.005819
+          0.700000
+          -0.054175 0.019367 -0.600026 -0.797909
+          0.070456 -0.150704 0.005820
+          0.733334
+          -0.160164 0.192311 -0.624371 -0.739949
+          0.070455 -0.150704 0.005820
+          0.766667
+          -0.222687 0.356466 -0.607560 -0.673953
+          0.070455 -0.150703 0.005820
+          0.800000
+          -0.203622 0.501081 -0.595607 -0.593893
+          0.070456 -0.150704 0.005819
+          0.833334
+          -0.095657 0.587065 -0.579346 -0.557281
+          0.070456 -0.150703 0.005819
+          0.866667
+          -0.026464 -0.568984 0.572103 0.590132
+          0.070456 -0.150704 0.005819
+          0.900000
+          -0.090442 -0.491787 0.557178 0.662962
+          0.070456 -0.150702 0.005819
+          0.933334
+          -0.045974 -0.405310 0.554861 0.725079
+          0.070456 -0.150704 0.005820
+          0.966667
+          0.106936 -0.305158 0.566496 0.757975
+          0.070455 -0.150704 0.005819
+          1.000000
+          0.251047 -0.127825 0.581785 0.762996
+          0.070455 -0.150703 0.005820
+          1.033334
+          0.338982 0.068066 0.572150 0.743709
+          0.070456 -0.150704 0.005820
+          1.066667
+          0.334094 0.205336 0.571894 0.720525
+          0.070456 -0.150704 0.005819
+          1.100000
+          0.314080 0.315249 0.528538 0.722925
+          0.070456 -0.150703 0.005820
+          1.133334
+          -0.271600 -0.394387 -0.472114 -0.740136
+          0.070456 -0.150703 0.005819
+          1.166667
+          -0.144597 -0.358735 -0.462186 -0.797988
+          0.070455 -0.150703 0.005819
+          1.200000
+          -0.062999 -0.214632 -0.520651 -0.823946
+          0.070456 -0.150702 0.005820
+          1.233333
+          -0.090813 -0.036813 -0.599544 -0.794321
+          0.070456 -0.150702 0.005819
+          1.266667
+          -0.195829 0.155671 -0.618152 -0.745188
+          0.070455 -0.150704 0.005820
+          1.300000
+          -0.252491 0.338390 -0.597313 -0.681879
+          0.070456 -0.150703 0.005819
+          1.333333
+          -0.225123 0.498149 -0.584504 -0.599603
+          0.070455 -0.150702 0.005819
+          1.366667
+          -0.107876 0.593905 -0.565921 -0.561581
+          0.070456 -0.150702 0.005820
+          1.400000
+          -0.017178 -0.584619 0.558922 0.587820
+          0.070455 -0.150702 0.005819
+          1.433333
+          -0.081161 -0.519637 0.550836 0.648052
+          0.070455 -0.150704 0.005819
+          1.466667
+          -0.043045 -0.433648 0.550470 0.712095
+          0.070455 -0.150704 0.005819
+          1.500000
+          0.094238 -0.326168 0.549157 0.763649
+          0.070454 -0.150704 0.005819
+          1.533333
+          0.228578 -0.171306 0.568474 0.771521
+          0.070456 -0.150704 0.005819
+          1.566666
+          0.307214 0.008954 0.607429 0.732509
+          0.070455 -0.150704 0.005819
+          1.600000
+      "L_Arm_sjnt_1"
+      49
+          0.521735 0.051347 -0.008184 0.851521
+          -0.000001 -0.260631 -0.000000
+          0.000000
+          0.607730 0.017648 -0.032058 0.793300
+          -0.000001 -0.260631 -0.000000
+          0.033333
+          0.677096 0.025478 -0.027141 0.734952
+          -0.000001 -0.260631 -0.000000
+          0.066667
+          0.732769 0.055296 0.005536 0.678204
+          -0.000001 -0.260631 -0.000000
+          0.100000
+          0.762073 0.063121 0.018307 0.644146
+          -0.000001 -0.260631 -0.000000
+          0.133333
+          0.773812 0.041133 -0.006889 0.632041
+          -0.000001 -0.260631 -0.000000
+          0.166667
+          0.704942 0.024994 -0.027858 0.708276
+          -0.000001 -0.260631 -0.000000
+          0.200000
+          0.510519 0.001021 -0.037474 0.859049
+          -0.000001 -0.260631 -0.000000
+          0.233333
+          0.307765 0.041541 -0.009750 0.950505
+          -0.000001 -0.260631 -0.000000
+          0.266667
+          0.281875 0.034209 -0.011103 0.958777
+          -0.000001 -0.260631 0.000000
+          0.300000
+          0.339027 0.053760 -0.006320 0.939218
+          -0.000001 -0.260631 0.000000
+          0.333333
+          0.385427 0.085036 0.005901 0.918793
+          -0.000001 -0.260631 0.000000
+          0.366667
+          0.424480 0.160285 0.043988 0.890051
+          -0.000001 -0.260631 0.000000
+          0.400000
+          0.436682 0.187648 0.059612 0.877806
+          -0.000001 -0.260631 0.000000
+          0.433333
+          0.423498 0.176407 0.050820 0.887100
+          -0.000001 -0.260631 -0.000000
+          0.466667
+          0.441110 0.105078 0.017398 0.891110
+          -0.000001 -0.260631 -0.000000
+          0.500000
+          0.521735 0.051347 -0.008185 0.851522
+          -0.000001 -0.260631 -0.000000
+          0.533333
+          0.607730 0.017648 -0.032058 0.793300
+          -0.000001 -0.260631 -0.000000
+          0.566667
+          0.677096 0.025478 -0.027141 0.734952
+          -0.000001 -0.260631 -0.000000
+          0.600000
+          0.732769 0.055296 0.005536 0.678204
+          -0.000001 -0.260631 -0.000000
+          0.633333
+          0.762073 0.063121 0.018306 0.644146
+          -0.000001 -0.260631 -0.000001
+          0.666667
+          0.773812 0.041133 -0.006889 0.632041
+          -0.000001 -0.260632 -0.000001
+          0.700000
+          0.704941 0.024994 -0.027858 0.708277
+          -0.000001 -0.260631 0.000000
+          0.733334
+          0.510518 0.001021 -0.037474 0.859049
+          -0.000002 -0.260631 -0.000000
+          0.766667
+          0.307765 0.041541 -0.009750 0.950505
+          -0.000001 -0.260631 -0.000000
+          0.800000
+          0.281876 0.034209 -0.011103 0.958776
+          -0.000001 -0.260631 0.000000
+          0.833334
+          0.339028 0.053760 -0.006320 0.939218
+          -0.000001 -0.260631 0.000000
+          0.866667
+          0.385427 0.085037 0.005901 0.918793
+          -0.000001 -0.260632 0.000000
+          0.900000
+          0.424481 0.160285 0.043988 0.890050
+          -0.000001 -0.260631 0.000000
+          0.933334
+          0.436682 0.187648 0.059612 0.877806
+          -0.000001 -0.260630 0.000000
+          0.966667
+          0.423498 0.176407 0.050819 0.887100
+          -0.000001 -0.260631 0.000000
+          1.000000
+          0.441110 0.105077 0.017397 0.891110
+          -0.000001 -0.260632 -0.000000
+          1.033334
+          0.521735 0.051347 -0.008185 0.851521
+          -0.000001 -0.260631 -0.000000
+          1.066667
+          0.607730 0.017648 -0.032058 0.793300
+          -0.000001 -0.260631 -0.000001
+          1.100000
+          0.677096 0.025478 -0.027141 0.734952
+          -0.000001 -0.260631 -0.000001
+          1.133334
+          0.732769 0.055296 0.005536 0.678204
+          -0.000001 -0.260631 0.000001
+          1.166667
+          0.762073 0.063121 0.018306 0.644146
+          -0.000001 -0.260633 0.000000
+          1.200000
+          0.773812 0.041133 -0.006889 0.632041
+          -0.000002 -0.260630 -0.000000
+          1.233333
+          0.704942 0.024994 -0.027858 0.708276
+          -0.000001 -0.260630 0.000000
+          1.266667
+          0.510519 0.001021 -0.037474 0.859049
+          -0.000001 -0.260632 -0.000000
+          1.300000
+          0.307765 0.041541 -0.009750 0.950505
+          0.000000 -0.260631 0.000000
+          1.333333
+          0.281876 0.034209 -0.011103 0.958776
+          -0.000001 -0.260632 -0.000000
+          1.366667
+          0.339027 0.053760 -0.006320 0.939218
+          -0.000001 -0.260632 0.000000
+          1.400000
+          0.385426 0.085036 0.005901 0.918793
+          -0.000001 -0.260631 0.000000
+          1.433333
+          0.424480 0.160284 0.043988 0.890051
+          -0.000001 -0.260632 0.000000
+          1.466667
+          0.436682 0.187648 0.059612 0.877806
+          -0.000001 -0.260631 0.000000
+          1.500000
+          0.423498 0.176408 0.050820 0.887100
+          -0.000001 -0.260632 0.000000
+          1.533333
+          0.441110 0.105078 0.017398 0.891110
+          -0.000001 -0.260631 -0.000000
+          1.566666
+          0.521734 0.051348 -0.008184 0.851522
+          -0.000001 -0.260631 -0.000001
+          1.600000
+      "L_Wrist_sjnt_0"
+      49
+          0.029405 -0.287262 -0.079056 0.954131
+          0.000007 -0.242273 0.000002
+          0.000000
+          0.033308 -0.287259 -0.073192 0.954471
+          0.000007 -0.242273 0.000002
+          0.033333
+          0.016710 -0.288243 -0.080526 0.954019
+          0.000007 -0.242273 0.000002
+          0.066667
+          -0.003318 -0.290685 -0.057085 0.955109
+          0.000007 -0.242273 0.000002
+          0.100000
+          0.021104 -0.288241 -0.078570 0.954096
+          0.000007 -0.242273 0.000003
+          0.133333
+          0.020608 -0.289213 -0.059518 0.955191
+          0.000007 -0.242273 0.000002
+          0.166667
+          0.024504 -0.289206 -0.034602 0.956327
+          0.000007 -0.242274 0.000002
+          0.200000
+          -0.014245 0.289694 0.022393 -0.956751
+          0.000007 -0.242273 0.000002
+          0.233333
+          -0.024344 -0.289695 0.015208 0.956689
+          0.000006 -0.242274 0.000002
+          0.266667
+          0.034251 -0.289680 0.011812 0.956438
+          0.000007 -0.242274 0.000002
+          0.300000
+          0.020088 -0.289194 0.018645 0.956878
+          0.000007 -0.242273 0.000002
+          0.333333
+          0.033278 -0.289194 0.003018 0.956687
+          0.000007 -0.242273 0.000002
+          0.366667
+          0.009862 -0.289702 -0.048286 0.955847
+          0.000007 -0.242274 0.000002
+          0.400000
+          0.004136 -0.288734 -0.077111 0.954290
+          0.000007 -0.242273 0.000002
+          0.433333
+          0.037219 -0.285796 -0.086381 0.953663
+          0.000007 -0.242273 0.000002
+          0.466667
+          0.004638 -0.285812 -0.114728 0.951382
+          0.000007 -0.242273 0.000002
+          0.500000
+          0.029405 -0.287262 -0.079056 0.954131
+          0.000007 -0.242274 0.000002
+          0.533333
+          0.033308 -0.287260 -0.073192 0.954471
+          0.000006 -0.242273 0.000002
+          0.566667
+          0.016710 -0.288243 -0.080526 0.954019
+          0.000007 -0.242272 0.000002
+          0.600000
+          -0.003318 -0.290685 -0.057085 0.955109
+          0.000007 -0.242273 0.000002
+          0.633333
+          0.021104 -0.288241 -0.078570 0.954096
+          0.000007 -0.242273 0.000002
+          0.666667
+          0.020608 -0.289213 -0.059518 0.955191
+          0.000007 -0.242273 0.000002
+          0.700000
+          0.024504 -0.289206 -0.034602 0.956327
+          0.000007 -0.242273 0.000002
+          0.733334
+          -0.014245 0.289694 0.022393 -0.956751
+          0.000007 -0.242273 0.000002
+          0.766667
+          -0.024343 -0.289695 0.015208 0.956689
+          0.000007 -0.242273 0.000002
+          0.800000
+          0.034251 -0.289680 0.011812 0.956438
+          0.000006 -0.242273 0.000002
+          0.833334
+          0.020088 -0.289194 0.018645 0.956878
+          0.000007 -0.242274 0.000002
+          0.866667
+          0.033278 -0.289194 0.003017 0.956687
+          0.000007 -0.242273 0.000003
+          0.900000
+          0.009861 -0.289702 -0.048286 0.955847
+          0.000006 -0.242273 0.000002
+          0.933334
+          0.004136 -0.288734 -0.077111 0.954290
+          0.000006 -0.242273 0.000002
+          0.966667
+          0.037219 -0.285796 -0.086381 0.953663
+          0.000007 -0.242273 0.000003
+          1.000000
+          0.004638 -0.285812 -0.114728 0.951382
+          0.000007 -0.242274 0.000002
+          1.033334
+          0.029404 -0.287262 -0.079056 0.954131
+          0.000007 -0.242275 0.000002
+          1.066667
+          0.033308 -0.287260 -0.073192 0.954471
+          0.000007 -0.242273 0.000002
+          1.100000
+          0.016710 -0.288243 -0.080526 0.954019
+          0.000007 -0.242273 0.000002
+          1.133334
+          -0.003318 -0.290685 -0.057085 0.955109
+          0.000007 -0.242274 0.000002
+          1.166667
+          0.021104 -0.288241 -0.078570 0.954096
+          0.000007 -0.242275 0.000003
+          1.200000
+          0.020608 -0.289213 -0.059518 0.955191
+          0.000007 -0.242272 0.000002
+          1.233333
+          0.024504 -0.289206 -0.034602 0.956327
+          0.000006 -0.242272 0.000003
+          1.266667
+          -0.014246 0.289694 0.022393 -0.956751
+          0.000006 -0.242273 0.000001
+          1.300000
+          -0.024344 -0.289695 0.015208 0.956689
+          0.000006 -0.242273 0.000003
+          1.333333
+          0.034251 -0.289680 0.011812 0.956438
+          0.000006 -0.242272 0.000003
+          1.366667
+          0.020088 -0.289194 0.018645 0.956878
+          0.000006 -0.242273 0.000003
+          1.400000
+          0.033278 -0.289194 0.003018 0.956687
+          0.000007 -0.242273 0.000002
+          1.433333
+          0.009862 -0.289702 -0.048286 0.955847
+          0.000007 -0.242273 0.000002
+          1.466667
+          0.004136 -0.288734 -0.077111 0.954290
+          0.000006 -0.242273 0.000002
+          1.500000
+          0.037219 -0.285796 -0.086381 0.953663
+          0.000006 -0.242272 0.000003
+          1.533333
+          0.004638 -0.285812 -0.114728 0.951382
+          0.000006 -0.242274 0.000002
+          1.566666
+          0.029404 -0.287262 -0.079056 0.954131
+          0.000006 -0.242272 0.000002
+          1.600000
+      "R_Clavicle_sjnt_0"
+      49
+          -0.176742 -0.108321 0.559369 0.802581
+          -0.007299 0.208857 0.030677
+          0.000000
+          -0.188948 -0.117108 0.552039 0.803640
+          -0.007299 0.208857 0.030677
+          0.033333
+          -0.176249 -0.104899 0.540806 0.815758
+          -0.007299 0.208857 0.030677
+          0.066667
+          -0.148413 -0.084391 0.534462 0.827770
+          -0.007299 0.208857 0.030677
+          0.100000
+          -0.134745 -0.076582 0.554982 0.817297
+          -0.007299 0.208857 0.030677
+          0.133333
+          -0.147939 -0.095632 0.588684 0.788936
+          -0.007299 0.208857 0.030677
+          0.166667
+          -0.158201 -0.108821 0.620434 0.760390
+          -0.007299 0.208857 0.030677
+          0.200000
+          -0.165530 -0.112730 0.634110 0.746858
+          -0.007299 0.208857 0.030677
+          0.233333
+          -0.161620 -0.098080 0.623854 0.758331
+          -0.007299 0.208857 0.030677
+          0.266667
+          -0.141101 -0.069264 0.588688 0.792931
+          -0.007299 0.208857 0.030677
+          0.300000
+          -0.143534 -0.072188 0.555470 0.815868
+          -0.007299 0.208857 0.030677
+          0.333333
+          -0.168925 -0.095623 0.547159 0.814210
+          -0.007299 0.208857 0.030677
+          0.366667
+          -0.186504 -0.115153 0.542758 0.810784
+          -0.007299 0.208857 0.030677
+          0.400000
+          -0.177715 -0.104900 0.546180 0.811851
+          -0.007299 0.208857 0.030677
+          0.433333
+          -0.151835 -0.077558 0.552537 0.815864
+          -0.007299 0.208857 0.030677
+          0.466667
+          -0.156231 -0.082930 0.555955 0.812175
+          -0.007299 0.208857 0.030677
+          0.500000
+          -0.178695 -0.106367 0.558880 0.802750
+          -0.007299 0.208857 0.030677
+          0.533333
+          -0.188949 -0.117597 0.552527 0.803233
+          -0.007299 0.208857 0.030677
+          0.566667
+          -0.175761 -0.105387 0.540806 0.815800
+          -0.007299 0.208857 0.030677
+          0.600000
+          -0.148413 -0.084879 0.534462 0.827720
+          -0.007299 0.208857 0.030677
+          0.633333
+          -0.134745 -0.076094 0.554982 0.817342
+          -0.007299 0.208857 0.030677
+          0.666667
+          -0.147938 -0.095632 0.588196 0.789300
+          -0.007299 0.208857 0.030677
+          0.700000
+          -0.158202 -0.108821 0.620434 0.760390
+          -0.007298 0.208857 0.030677
+          0.733334
+          -0.165530 -0.112730 0.634110 0.746858
+          -0.007299 0.208857 0.030677
+          0.766667
+          -0.161619 -0.098079 0.623854 0.758331
+          -0.007299 0.208857 0.030677
+          0.800000
+          -0.141101 -0.069264 0.588688 0.792931
+          -0.007299 0.208857 0.030677
+          0.833334
+          -0.143534 -0.072188 0.555470 0.815868
+          -0.007299 0.208858 0.030677
+          0.866667
+          -0.168926 -0.095623 0.547159 0.814210
+          -0.007299 0.208858 0.030677
+          0.900000
+          -0.186505 -0.115153 0.542758 0.810784
+          -0.007299 0.208857 0.030677
+          0.933334
+          -0.177715 -0.104900 0.546180 0.811851
+          -0.007299 0.208857 0.030677
+          0.966667
+          -0.151835 -0.077558 0.552537 0.815864
+          -0.007299 0.208857 0.030677
+          1.000000
+          -0.156231 -0.082930 0.555955 0.812175
+          -0.007299 0.208857 0.030677
+          1.033334
+          -0.178695 -0.106367 0.558880 0.802750
+          -0.007299 0.208857 0.030677
+          1.066667
+          -0.188948 -0.117596 0.552527 0.803233
+          -0.007299 0.208858 0.030677
+          1.100000
+          -0.175761 -0.105387 0.540806 0.815800
+          -0.007299 0.208858 0.030677
+          1.133334
+          -0.148413 -0.084879 0.534462 0.827719
+          -0.007299 0.208857 0.030677
+          1.166667
+          -0.134745 -0.076094 0.554982 0.817343
+          -0.007299 0.208858 0.030677
+          1.200000
+          -0.147938 -0.095631 0.588196 0.789300
+          -0.007299 0.208858 0.030677
+          1.233333
+          -0.158201 -0.108821 0.620434 0.760389
+          -0.007299 0.208858 0.030677
+          1.266667
+          -0.165530 -0.112730 0.634110 0.746857
+          -0.007300 0.208857 0.030677
+          1.300000
+          -0.161620 -0.098080 0.623854 0.758331
+          -0.007299 0.208858 0.030677
+          1.333333
+          -0.141101 -0.069264 0.588688 0.792931
+          -0.007299 0.208858 0.030677
+          1.366667
+          -0.143534 -0.072188 0.555470 0.815868
+          -0.007299 0.208857 0.030677
+          1.400000
+          -0.168925 -0.095623 0.547159 0.814210
+          -0.007299 0.208858 0.030677
+          1.433333
+          -0.186505 -0.115642 0.542758 0.810714
+          -0.007299 0.208857 0.030677
+          1.466667
+          -0.177715 -0.104900 0.546180 0.811851
+          -0.007299 0.208857 0.030677
+          1.500000
+          -0.152324 -0.077070 0.552537 0.815819
+          -0.007299 0.208857 0.030677
+          1.533333
+          -0.156719 -0.082441 0.555955 0.812131
+          -0.007299 0.208858 0.030676
+          1.566666
+          -0.176742 -0.108320 0.559369 0.802581
+          -0.007299 0.208858 0.030678
+          1.600000
+      "R_Arm_sjnt_0"
+      49
+          0.021884 -0.265057 0.636070 0.724349
+          -0.070458 0.150703 -0.005820
+          0.000000
+          -0.079253 -0.373020 0.633132 0.673587
+          -0.070458 0.150703 -0.005820
+          0.033333
+          -0.198452 -0.396958 0.616975 0.649911
+          -0.070458 0.150703 -0.005820
+          0.066667
+          -0.292230 -0.359344 0.583222 0.667327
+          -0.070457 0.150703 -0.005820
+          0.100000
+          -0.317606 -0.306101 0.540206 0.716663
+          -0.070457 0.150703 -0.005820
+          0.133333
+          -0.236987 -0.294379 0.530460 0.758809
+          -0.070458 0.150703 -0.005820
+          0.166667
+          -0.074318 -0.301218 0.557388 0.770106
+          -0.070458 0.150703 -0.005820
+          0.200000
+          0.131825 -0.267514 0.586768 0.752836
+          -0.070458 0.150703 -0.005820
+          0.233333
+          0.293036 -0.158582 0.590221 0.735269
+          -0.070457 0.150703 -0.005820
+          0.266667
+          -0.331146 0.031080 -0.591181 -0.734766
+          -0.070458 0.150703 -0.005820
+          0.300000
+          -0.289641 -0.071508 -0.567690 -0.767282
+          -0.070457 0.150703 -0.005820
+          0.333333
+          -0.189548 -0.167735 -0.475294 -0.842634
+          -0.070458 0.150703 -0.005820
+          0.366667
+          0.089936 0.137922 0.381953 0.909396
+          -0.070457 0.150703 -0.005820
+          0.400000
+          0.015678 -0.027193 0.379522 0.924650
+          -0.070458 0.150703 -0.005820
+          0.433333
+          0.004392 -0.166893 0.467978 0.867827
+          -0.070458 0.150703 -0.005820
+          0.466667
+          0.042936 -0.246020 0.553508 0.794519
+          -0.070457 0.150703 -0.005820
+          0.500000
+          0.023363 -0.328573 0.596027 0.732288
+          -0.070458 0.150703 -0.005820
+          0.533333
+          -0.068493 -0.416506 0.597972 0.681367
+          -0.070458 0.150703 -0.005820
+          0.566667
+          -0.192575 -0.448262 0.573510 0.658074
+          -0.070457 0.150703 -0.005820
+          0.600000
+          -0.283910 -0.401366 0.542687 0.681022
+          -0.070457 0.150703 -0.005820
+          0.633333
+          -0.304403 -0.334441 0.511883 0.730386
+          -0.070457 0.150702 -0.005819
+          0.666667
+          -0.224767 -0.313435 0.514836 0.765625
+          -0.070457 0.150703 -0.005820
+          0.700000
+          -0.066500 -0.309036 0.553974 0.770187
+          -0.070457 0.150702 -0.005819
+          0.733334
+          0.139151 -0.258719 0.592142 0.750379
+          -0.070457 0.150703 -0.005820
+          0.766667
+          0.302317 -0.130246 0.596080 0.732345
+          -0.070456 0.150703 -0.005820
+          0.800000
+          -0.345314 -0.012399 -0.590199 -0.729568
+          -0.070458 0.150703 -0.005820
+          0.833334
+          -0.293549 -0.132085 -0.568165 -0.757344
+          -0.070457 0.150703 -0.005820
+          0.866667
+          -0.176842 -0.240525 -0.484066 -0.822529
+          -0.070457 0.150703 -0.005820
+          0.900000
+          0.063057 0.218532 0.406351 0.884956
+          -0.070457 0.150703 -0.005820
+          0.933334
+          -0.007297 0.061236 0.419064 0.905860
+          -0.070457 0.150703 -0.005820
+          0.966667
+          -0.002948 -0.067714 0.509966 0.857520
+          -0.070457 0.150702 -0.005820
+          1.000000
+          0.049762 -0.140979 0.597452 0.787845
+          -0.070458 0.150703 -0.005820
+          1.033334
+          0.042887 -0.226950 0.649747 0.724212
+          -0.070457 0.150702 -0.005819
+          1.066667
+          -0.042624 -0.325628 0.662933 0.672807
+          -0.070457 0.150703 -0.005820
+          1.100000
+          -0.170128 -0.368618 0.645313 0.647108
+          -0.070457 0.150703 -0.005820
+          1.133334
+          -0.271723 -0.327584 0.613510 0.665177
+          -0.070458 0.150703 -0.005820
+          1.166667
+          -0.299053 -0.265547 0.574400 0.714225
+          -0.070457 0.150702 -0.005819
+          1.200000
+          -0.218436 -0.250893 0.566607 0.753854
+          -0.070459 0.150704 -0.005820
+          1.233333
+          -0.057719 -0.252360 0.590114 0.764689
+          -0.070457 0.150703 -0.005819
+          1.266667
+          0.143542 -0.210352 0.612162 0.748603
+          -0.070457 0.150702 -0.005819
+          1.300000
+          0.296454 -0.094583 0.603397 0.734221
+          -0.070457 0.150702 -0.005819
+          1.333333
+          -0.330660 -0.031939 -0.591166 -0.734960
+          -0.070458 0.150703 -0.005820
+          1.366667
+          -0.274988 -0.135993 -0.567670 -0.763962
+          -0.070457 0.150703 -0.005820
+          1.400000
+          -0.160724 -0.231732 -0.480155 -0.830614
+          -0.070457 0.150702 -0.005819
+          1.433333
+          0.052803 0.198014 0.397559 0.894399
+          -0.070456 0.150703 -0.005820
+          1.466667
+          -0.016572 0.030459 0.403434 0.914351
+          -0.070459 0.150703 -0.005820
+          1.500000
+          -0.020528 -0.103378 0.489448 0.865640
+          -0.070457 0.150702 -0.005820
+          1.533333
+          0.030715 -0.164917 0.582794 0.795116
+          -0.070457 0.150702 -0.005819
+          1.566666
+          0.021884 -0.265056 0.636069 0.724349
+          -0.070458 0.150703 -0.005820
+          1.600000
+      "R_Arm_sjnt_1"
+      49
+          0.179870 -0.389391 -0.094624 0.898370
+          -0.000001 0.260632 -0.000000
+          0.000000
+          0.241437 -0.388908 -0.128793 0.879699
+          -0.000001 0.260632 0.000000
+          0.033333
+          0.288829 -0.366926 -0.147834 0.871830
+          -0.000001 0.260632 0.000000
+          0.066667
+          0.318138 -0.341032 -0.152230 0.871385
+          -0.000001 0.260632 0.000000
+          0.100000
+          0.344014 -0.277514 -0.136128 0.886628
+          -0.000001 0.260632 -0.000000
+          0.133333
+          0.386473 -0.127999 -0.084396 0.909468
+          -0.000001 0.260632 -0.000000
+          0.166667
+          0.434817 -0.061055 -0.062420 0.896276
+          -0.000001 0.260632 -0.000000
+          0.200000
+          -0.477311 0.031245 0.053132 -0.876570
+          -0.000001 0.260632 -0.000000
+          0.233333
+          -0.553978 -0.050370 0.007699 -0.830971
+          -0.000001 0.260632 -0.000000
+          0.266667
+          0.666334 0.054788 -0.000805 0.743637
+          -0.000001 0.260632 -0.000001
+          0.300000
+          0.776760 0.016686 -0.036880 0.628495
+          -0.000001 0.260633 -0.000000
+          0.333333
+          0.835380 0.020118 -0.032452 0.548346
+          -0.000001 0.260632 -0.000000
+          0.366667
+          -0.829039 -0.006430 0.051013 -0.556823
+          -0.000001 0.260633 -0.000000
+          0.400000
+          -0.737214 0.024376 0.082315 -0.670183
+          -0.000000 0.260632 0.000000
+          0.433333
+          -0.524239 0.101117 0.102418 -0.839321
+          -0.000000 0.260633 -0.000000
+          0.466667
+          0.221397 -0.368385 -0.109271 0.896290
+          -0.000001 0.260633 -0.000000
+          0.500000
+          0.179870 -0.389391 -0.094624 0.898370
+          -0.000000 0.260632 -0.000000
+          0.533333
+          0.241438 -0.388908 -0.128793 0.879699
+          -0.000001 0.260633 0.000000
+          0.566667
+          0.288829 -0.366926 -0.147834 0.871830
+          -0.000001 0.260632 0.000000
+          0.600000
+          0.318138 -0.341032 -0.152230 0.871385
+          -0.000001 0.260633 0.000000
+          0.633333
+          0.344014 -0.277514 -0.136128 0.886628
+          -0.000001 0.260632 -0.000000
+          0.666667
+          0.386473 -0.127999 -0.084396 0.909468
+          -0.000001 0.260633 -0.000000
+          0.700000
+          0.434818 -0.061055 -0.062420 0.896275
+          -0.000001 0.260633 -0.000000
+          0.733334
+          -0.477311 0.031244 0.053132 -0.876570
+          -0.000001 0.260633 -0.000000
+          0.766667
+          -0.553978 -0.050370 0.007699 -0.830970
+          -0.000001 0.260633 -0.000000
+          0.800000
+          0.666335 0.054788 -0.000805 0.743637
+          -0.000001 0.260632 -0.000000
+          0.833334
+          0.776760 0.016686 -0.036880 0.628494
+          -0.000001 0.260632 0.000000
+          0.866667
+          0.835380 0.020118 -0.032452 0.548346
+          -0.000001 0.260633 0.000000
+          0.900000
+          -0.829038 -0.006430 0.051013 -0.556824
+          -0.000000 0.260632 -0.000001
+          0.933334
+          -0.737212 0.024377 0.082315 -0.670185
+          0.000000 0.260633 0.000000
+          0.966667
+          -0.524236 0.101120 0.102418 -0.839322
+          -0.000001 0.260632 -0.000000
+          1.000000
+          0.221397 -0.368385 -0.109271 0.896290
+          0.000000 0.260633 0.000000
+          1.033334
+          0.179870 -0.389391 -0.094624 0.898369
+          -0.000001 0.260633 0.000000
+          1.066667
+          0.241438 -0.388908 -0.128793 0.879699
+          -0.000000 0.260632 -0.000000
+          1.100000
+          0.288829 -0.366926 -0.147834 0.871830
+          -0.000001 0.260632 0.000000
+          1.133334
+          0.318138 -0.341032 -0.152230 0.871385
+          -0.000001 0.260633 -0.000000
+          1.166667
+          0.344014 -0.277514 -0.136128 0.886628
+          -0.000001 0.260632 -0.000000
+          1.200000
+          0.386473 -0.127999 -0.084396 0.909468
+          -0.000001 0.260632 -0.000000
+          1.233333
+          0.434817 -0.061055 -0.062420 0.896276
+          -0.000001 0.260632 -0.000000
+          1.266667
+          -0.477310 0.031245 0.053132 -0.876570
+          -0.000001 0.260632 -0.000001
+          1.300000
+          -0.553978 -0.050370 0.007699 -0.830971
+          -0.000001 0.260633 -0.000001
+          1.333333
+          0.666333 0.054788 -0.000805 0.743638
+          -0.000001 0.260633 -0.000001
+          1.366667
+          0.776759 0.016686 -0.036880 0.628495
+          -0.000001 0.260632 0.000000
+          1.400000
+          0.835379 0.020118 -0.032452 0.548346
+          -0.000000 0.260632 0.000001
+          1.433333
+          -0.829039 -0.006430 0.051013 -0.556823
+          -0.000001 0.260633 0.000000
+          1.466667
+          -0.737215 0.024376 0.082315 -0.670182
+          -0.000001 0.260633 -0.000000
+          1.500000
+          -0.524241 0.101117 0.102418 -0.839320
+          0.000000 0.260632 0.000000
+          1.533333
+          0.221400 -0.368383 -0.109271 0.896290
+          -0.000001 0.260632 0.000000
+          1.566666
+          0.179870 -0.389391 -0.094624 0.898370
+          0.000000 0.260632 0.000000
+          1.600000
+      "R_Wrist_sjnt_0"
+      49
+          -0.008907 -0.087873 0.045934 0.995032
+          0.000000 0.242268 -0.000000
+          0.000000
+          0.063483 -0.091762 0.042057 0.992865
+          0.000000 0.242268 -0.000000
+          0.033333
+          0.084958 -0.094675 0.068434 0.989513
+          0.000000 0.242268 0.000000
+          0.066667
+          0.057144 -0.090797 0.020570 0.994016
+          0.000000 0.242268 -0.000000
+          0.100000
+          0.084016 -0.087881 -0.025563 0.992252
+          0.000000 0.242268 0.000000
+          0.133333
+          0.029337 -0.087415 -0.045848 0.994684
+          0.000000 0.242268 -0.000000
+          0.166667
+          0.083049 -0.084963 -0.052176 0.991545
+          0.000000 0.242268 -0.000001
+          0.200000
+          0.032782 -0.080611 -0.120069 0.988944
+          0.000000 0.242268 -0.000001
+          0.233333
+          -0.008104 -0.084048 -0.138638 0.986737
+          0.000000 0.242268 -0.000001
+          0.266667
+          0.009971 -0.080147 -0.162070 0.983469
+          0.000000 0.242268 -0.000000
+          0.300000
+          -0.012240 -0.083576 -0.173796 0.981152
+          0.000000 0.242268 -0.000000
+          0.333333
+          -0.009794 -0.083581 -0.185514 0.979032
+          0.000000 0.242269 -0.000000
+          0.366667
+          -0.020178 -0.087480 -0.164032 0.982361
+          0.000000 0.242268 -0.000000
+          0.400000
+          -0.016789 -0.090374 -0.085904 0.992054
+          0.000000 0.242269 -0.000000
+          0.433333
+          0.040498 0.092791 0.016576 -0.994724
+          0.000000 0.242268 -0.000000
+          0.466667
+          0.005748 -0.089341 0.029094 0.995560
+          0.000000 0.242268 -0.000000
+          0.500000
+          -0.008907 -0.087873 0.045934 0.995032
+          -0.000000 0.242269 -0.000000
+          0.533333
+          0.063483 -0.091762 0.042058 0.992865
+          -0.000000 0.242269 -0.000000
+          0.566667
+          0.084958 -0.094675 0.068434 0.989513
+          0.000000 0.242267 -0.000000
+          0.600000
+          0.057144 -0.090797 0.020570 0.994016
+          0.000000 0.242268 -0.000001
+          0.633333
+          0.084016 -0.087881 -0.025563 0.992252
+          0.000000 0.242268 -0.000000
+          0.666667
+          0.029337 -0.087415 -0.045848 0.994684
+          0.000000 0.242268 -0.000000
+          0.700000
+          0.083049 -0.084963 -0.052176 0.991545
+          0.000000 0.242269 -0.000000
+          0.733334
+          0.032782 -0.080611 -0.120069 0.988944
+          0.000000 0.242268 -0.000001
+          0.766667
+          -0.008103 -0.084048 -0.138638 0.986737
+          0.000000 0.242268 -0.000000
+          0.800000
+          0.009971 -0.080147 -0.162070 0.983469
+          0.000000 0.242268 -0.000000
+          0.833334
+          -0.012240 -0.083576 -0.173796 0.981152
+          0.000000 0.242269 -0.000000
+          0.866667
+          -0.009794 -0.083581 -0.185514 0.979032
+          -0.000000 0.242269 -0.000000
+          0.900000
+          -0.020178 -0.087480 -0.164031 0.982361
+          0.000000 0.242269 -0.000000
+          0.933334
+          -0.016790 -0.090374 -0.085903 0.992054
+          0.000000 0.242268 -0.000001
+          0.966667
+          0.040497 0.092791 0.016576 -0.994724
+          0.000000 0.242269 -0.000000
+          1.000000
+          0.005748 -0.089341 0.029095 0.995559
+          0.000000 0.242269 -0.000001
+          1.033334
+          -0.008906 -0.087873 0.045934 0.995032
+          0.000001 0.242268 -0.000000
+          1.066667
+          0.063483 -0.091762 0.042058 0.992865
+          0.000000 0.242269 -0.000000
+          1.100000
+          0.084958 -0.094675 0.068434 0.989513
+          0.000000 0.242268 -0.000000
+          1.133334
+          0.057144 -0.090797 0.020570 0.994016
+          -0.000000 0.242269 -0.000000
+          1.166667
+          0.084016 -0.087881 -0.025563 0.992252
+          0.000000 0.242268 -0.000000
+          1.200000
+          0.029337 -0.087415 -0.045848 0.994684
+          0.000000 0.242269 -0.000000
+          1.233333
+          0.083050 -0.084963 -0.052176 0.991545
+          0.000000 0.242267 -0.000000
+          1.266667
+          0.032782 -0.080611 -0.120069 0.988944
+          0.000000 0.242269 -0.000001
+          1.300000
+          -0.008104 -0.084048 -0.138638 0.986737
+          0.000000 0.242270 -0.000001
+          1.333333
+          0.009971 -0.080147 -0.162070 0.983469
+          0.000000 0.242269 -0.000000
+          1.366667
+          -0.012240 -0.083576 -0.173796 0.981152
+          0.000000 0.242269 -0.000000
+          1.400000
+          -0.009794 -0.083581 -0.185514 0.979032
+          0.000000 0.242268 -0.000000
+          1.433333
+          -0.020178 -0.087480 -0.164032 0.982361
+          0.000000 0.242269 -0.000000
+          1.466667
+          -0.016790 -0.090374 -0.085905 0.992054
+          0.000000 0.242268 0.000000
+          1.500000
+          0.040497 0.092791 0.016577 -0.994724
+          0.000000 0.242269 -0.000001
+          1.533333
+          0.005748 -0.089341 0.029094 0.995560
+          0.000000 0.242269 -0.000001
+          1.566666
+          -0.008907 -0.087872 0.045934 0.995032
+          0.000001 0.242267 -0.000000
+          1.600000
+      "C_Neck_sjnt_0"
+      49
+          0.153323 -0.043470 0.052720 0.985811
+          0.000000 0.206349 -0.038118
+          0.000000
+          0.157717 -0.026379 0.047350 0.985996
+          -0.000000 0.206349 -0.038118
+          0.033333
+          0.184083 -0.010258 0.015111 0.982741
+          0.000000 0.206349 -0.038118
+          0.066667
+          0.188965 -0.004266 -0.032762 0.981428
+          -0.000000 0.206349 -0.038118
+          0.100000
+          0.213379 -0.002548 -0.070866 0.974393
+          -0.000000 0.206349 -0.038118
+          0.133333
+          0.229004 0.004349 -0.073307 0.970652
+          0.000000 0.206349 -0.038118
+          0.166667
+          0.218261 0.013077 -0.072817 0.973082
+          0.000000 0.206349 -0.038118
+          0.200000
+          0.203613 0.012592 -0.089915 0.974833
+          0.000000 0.206349 -0.038118
+          0.233333
+          0.201172 0.007162 -0.102129 0.974191
+          0.000000 0.206349 -0.038118
+          0.266667
+          0.183594 -0.010111 -0.102133 0.977630
+          0.000000 0.206349 -0.038118
+          0.300000
+          0.186036 -0.024156 -0.070871 0.979686
+          -0.000000 0.206349 -0.038118
+          0.333333
+          0.204103 -0.039058 -0.022022 0.977922
+          0.000000 0.206349 -0.038118
+          0.366667
+          0.202639 -0.046876 0.004845 0.978119
+          0.000000 0.206349 -0.038118
+          0.400000
+          0.198244 -0.048832 0.014615 0.978826
+          0.000000 0.206349 -0.038118
+          0.433333
+          0.196779 -0.042976 0.033180 0.978943
+          -0.000000 0.206349 -0.038118
+          0.466667
+          0.195803 -0.038584 0.043928 0.978899
+          0.000000 0.206349 -0.038118
+          0.500000
+          0.178713 -0.035168 0.047349 0.982132
+          0.000000 0.206349 -0.038118
+          0.533333
+          0.183107 -0.018077 0.042955 0.981988
+          0.000000 0.206349 -0.038118
+          0.566667
+          0.208497 -0.002413 0.009251 0.977976
+          -0.000000 0.206349 -0.038118
+          0.600000
+          0.211426 0.002023 -0.040089 0.976569
+          0.000000 0.206349 -0.038118
+          0.633333
+          0.232910 0.001787 -0.078193 0.969348
+          0.000000 0.206349 -0.038118
+          0.666667
+          0.246582 0.005755 -0.079169 0.965866
+          0.000000 0.206349 -0.038118
+          0.700000
+          0.232910 0.011125 -0.077214 0.969364
+          0.000000 0.206349 -0.038118
+          0.733334
+          0.215332 0.007405 -0.092848 0.972089
+          0.000000 0.206349 -0.038118
+          0.766667
+          0.209472 -0.000150 -0.104085 0.972259
+          -0.000000 0.206349 -0.038118
+          0.800000
+          0.188965 -0.016092 -0.103600 0.976371
+          -0.000000 0.206348 -0.038118
+          0.833334
+          0.187989 -0.029771 -0.068918 0.979298
+          0.000000 0.206349 -0.038118
+          0.866667
+          0.202638 -0.041989 -0.017137 0.978203
+          0.000000 0.206349 -0.038118
+          0.900000
+          0.196779 -0.049320 0.011684 0.979137
+          0.000000 0.206349 -0.038118
+          0.933334
+          0.188967 -0.050787 0.021942 0.980424
+          0.000000 0.206349 -0.038118
+          0.966667
+          0.184084 -0.044443 0.040507 0.981069
+          0.000000 0.206349 -0.038118
+          1.000000
+          0.180178 -0.040051 0.050767 0.981506
+          0.000000 0.206349 -0.038118
+          1.033334
+          0.160158 -0.037122 0.053699 0.984930
+          0.000000 0.206348 -0.038118
+          1.066667
+          0.162599 -0.021985 0.048328 0.985263
+          -0.000000 0.206350 -0.038118
+          1.100000
+          0.187013 -0.008305 0.014623 0.982213
+          -0.000000 0.206349 -0.038118
+          1.133334
+          0.189454 -0.005487 -0.034228 0.981278
+          -0.000000 0.206349 -0.038118
+          1.166667
+          0.210938 -0.006088 -0.071844 0.974837
+          0.000000 0.206349 -0.038118
+          1.200000
+          0.225098 -0.000778 -0.073309 0.971574
+          0.000000 0.206349 -0.038118
+          1.233333
+          0.212402 0.006180 -0.071842 0.974518
+          0.000000 0.206349 -0.038118
+          1.266667
+          0.196289 0.003833 -0.086986 0.976673
+          0.000000 0.206350 -0.038118
+          1.300000
+          0.192871 -0.002207 -0.098712 0.976244
+          0.000000 0.206349 -0.038118
+          1.333333
+          0.173340 -0.017802 -0.098715 0.979741
+          0.000000 0.206349 -0.038118
+          1.366667
+          0.172853 -0.031237 -0.065987 0.982238
+          -0.000000 0.206349 -0.038118
+          1.400000
+          0.187502 -0.045896 -0.016650 0.981050
+          0.000000 0.206349 -0.038117
+          1.433333
+          0.183108 -0.054202 0.010217 0.981544
+          0.000000 0.206349 -0.038118
+          1.466667
+          0.176272 -0.056158 0.019987 0.982535
+          0.000000 0.206348 -0.038118
+          1.500000
+          0.173342 -0.049814 0.037575 0.982883
+          0.000000 0.206348 -0.038118
+          1.533333
+          0.170901 -0.045422 0.047835 0.983078
+          0.000000 0.206349 -0.038119
+          1.566666
+          0.153323 -0.043470 0.052720 0.985811
+          0.000000 0.206348 -0.038118
+          1.600000
+      "C_Neck_sjnt_1"
+      49
+          -0.241209 -0.035169 0.050280 0.968532
+          0.000000 0.054001 0.000000
+          0.000000
+          -0.238280 -0.021250 0.034161 0.970363
+          -0.000000 0.054001 0.000000
+          0.033333
+          -0.227539 -0.006046 0.009739 0.973701
+          0.000000 0.054001 0.000001
+          0.066667
+          -0.222658 0.009770 -0.024454 0.974541
+          -0.000000 0.054001 0.000000
+          0.100000
+          -0.213869 0.021251 -0.047412 0.975480
+          0.000000 0.054001 0.000000
+          0.133333
+          -0.208499 0.029064 -0.051807 0.976217
+          -0.000000 0.054001 0.000000
+          0.166667
+          -0.211917 0.034192 -0.054249 0.975182
+          0.000000 0.054001 0.000000
+          0.200000
+          -0.217288 0.034681 -0.060600 0.973607
+          0.000000 0.054001 0.000000
+          0.233333
+          -0.218265 0.032728 -0.061577 0.973395
+          0.000000 0.054000 0.000000
+          0.266667
+          -0.217775 0.017590 -0.054252 0.974331
+          0.000000 0.054000 0.000000
+          0.300000
+          -0.219727 -0.000398 -0.030807 0.975075
+          -0.000000 0.054000 0.000000
+          0.333333
+          -0.215819 -0.023680 -0.005409 0.976131
+          -0.000000 0.054001 0.000001
+          0.366667
+          -0.216794 -0.036623 0.009732 0.975482
+          0.000000 0.054000 0.000000
+          0.400000
+          -0.216306 -0.040044 0.024387 0.975199
+          0.000000 0.054001 0.000000
+          0.433333
+          -0.215818 -0.036629 0.038066 0.975004
+          0.000000 0.054000 0.000000
+          0.466667
+          -0.213865 -0.032724 0.045395 0.975259
+          -0.000000 0.054000 0.000000
+          0.500000
+          -0.216795 -0.028330 0.044419 0.974795
+          0.000000 0.054001 0.000000
+          0.533333
+          -0.213378 -0.014532 0.029278 0.976423
+          0.000000 0.054001 0.000000
+          0.566667
+          -0.203126 0.001099 0.003878 0.979144
+          0.000000 0.054000 0.000000
+          0.600000
+          -0.200197 0.016610 -0.030315 0.979146
+          -0.000000 0.054001 0.000000
+          0.633333
+          -0.194339 0.026625 -0.053762 0.979098
+          -0.000000 0.054001 0.000000
+          0.666667
+          -0.190921 0.031265 -0.057181 0.979440
+          -0.000000 0.054001 0.000000
+          0.700000
+          -0.197269 0.032241 -0.058157 0.978092
+          0.000000 0.054000 0.000000
+          0.733334
+          -0.205081 0.030044 -0.063043 0.976250
+          -0.000000 0.054000 0.000000
+          0.766667
+          -0.208987 0.025405 -0.064021 0.975490
+          -0.000000 0.054000 0.000001
+          0.800000
+          -0.212404 0.011365 -0.055719 0.975526
+          0.000000 0.054001 0.000000
+          0.833334
+          -0.217285 -0.006464 -0.028854 0.975660
+          -0.000000 0.054000 0.000000
+          0.866667
+          -0.217772 -0.027344 -0.000036 0.975617
+          0.000000 0.054000 0.000000
+          0.900000
+          -0.222165 -0.039067 0.017060 0.974077
+          0.000000 0.054001 0.000000
+          0.933334
+          -0.225095 -0.041512 0.033180 0.972887
+          -0.000000 0.054000 0.000000
+          0.966667
+          -0.228025 -0.037120 0.046860 0.971818
+          0.000000 0.054000 0.000001
+          1.000000
+          -0.229002 -0.033216 0.054188 0.971349
+          0.000000 0.054000 0.000000
+          1.033334
+          -0.234862 -0.029066 0.052235 0.970189
+          -0.000000 0.054000 0.000001
+          1.066667
+          -0.233398 -0.016855 0.035628 0.971582
+          -0.000000 0.054001 0.000000
+          1.100000
+          -0.224610 -0.004153 0.009250 0.974396
+          -0.000000 0.054001 0.000000
+          1.133334
+          -0.222169 0.008062 -0.025432 0.974643
+          0.000000 0.054001 0.000000
+          1.166667
+          -0.215822 0.016612 -0.048390 0.975092
+          -0.000000 0.054000 0.000000
+          1.200000
+          -0.212405 0.022961 -0.051809 0.975537
+          0.000000 0.054001 0.000000
+          1.233333
+          -0.217288 0.025890 -0.053762 0.974282
+          -0.000000 0.054000 0.000000
+          1.266667
+          -0.223635 0.024670 -0.058159 0.972623
+          0.000000 0.054000 0.000000
+          1.300000
+          -0.225588 0.021740 -0.059137 0.972183
+          -0.000000 0.054001 0.000000
+          1.333333
+          -0.227540 0.008433 -0.051812 0.972353
+          0.000000 0.054000 0.000000
+          1.366667
+          -0.231933 -0.009029 -0.026413 0.972331
+          0.000000 0.054001 0.000000
+          1.400000
+          -0.231443 -0.031251 -0.000037 0.972346
+          0.000000 0.053999 0.000000
+          1.433333
+          -0.235348 -0.043950 0.016081 0.970784
+          0.000000 0.054001 0.000000
+          1.466667
+          -0.237301 -0.046884 0.030736 0.969817
+          -0.000000 0.054001 0.000000
+          1.500000
+          -0.238278 -0.042492 0.042950 0.969316
+          0.000000 0.054002 0.000000
+          1.533333
+          -0.237790 -0.038099 0.050279 0.969266
+          -0.000000 0.054000 0.000000
+          1.566666
+          -0.241209 -0.035169 0.050280 0.968532
+          0.000000 0.054000 0.000000
+          1.600000
+      "C_Neck_sjnt_2"
+      49
+          0.007816 -0.039067 0.026359 0.998858
+          0.000000 0.054001 0.000000
+          0.000000
+          0.008425 -0.022220 0.019527 0.999527
+          0.000000 0.054001 0.000000
+          0.033333
+          0.008301 -0.003451 0.014648 0.999852
+          0.000000 0.054001 0.000000
+          0.066667
+          0.014646 0.016602 0.006352 0.999735
+          0.000000 0.054001 0.000000
+          0.100000
+          0.012325 0.031007 -0.000237 0.999443
+          -0.000000 0.054001 -0.000000
+          0.133333
+          0.011348 0.040041 -0.004874 0.999122
+          0.000000 0.054001 0.000000
+          0.166667
+          0.015620 0.045902 -0.009267 0.998781
+          0.000000 0.054001 0.000000
+          0.200000
+          0.016840 0.047856 -0.009755 0.998665
+          0.000000 0.054001 0.000000
+          0.233333
+          0.016108 0.045902 -0.008779 0.998778
+          -0.000000 0.054001 -0.000000
+          0.266667
+          0.023190 0.029789 -0.004632 0.999276
+          0.000000 0.054001 0.000000
+          0.300000
+          0.025634 0.008670 -0.000272 0.999634
+          0.000000 0.054001 0.000000
+          0.333333
+          0.027834 -0.018308 0.004635 0.999434
+          0.000000 0.054001 0.000000
+          0.366667
+          0.029301 -0.033690 0.008294 0.998968
+          0.000000 0.054000 0.000000
+          0.400000
+          0.031010 -0.040038 0.013176 0.998630
+          0.000000 0.054001 0.000000
+          0.433333
+          0.030278 -0.039551 0.017570 0.998604
+          -0.000000 0.054001 0.000000
+          0.466667
+          0.030033 -0.037110 0.020501 0.998649
+          0.000000 0.054001 0.000000
+          0.500000
+          0.033206 -0.032227 0.020502 0.998718
+          -0.000000 0.054001 0.000000
+          0.533333
+          0.033693 -0.016113 0.015134 0.999188
+          0.000000 0.054001 0.000000
+          0.566667
+          0.033691 0.003572 0.011232 0.999363
+          -0.000000 0.054001 -0.000000
+          0.600000
+          0.037595 0.024174 0.003058 0.998996
+          -0.000000 0.054001 -0.000000
+          0.633333
+          0.032710 0.037602 -0.003897 0.998750
+          -0.000000 0.054001 -0.000000
+          0.666667
+          0.029536 0.042974 -0.008291 0.998605
+          0.000000 0.054001 -0.000000
+          0.700000
+          0.031245 0.045416 -0.012197 0.998405
+          -0.000000 0.054001 0.000000
+          0.733334
+          0.029780 0.043463 -0.013174 0.998524
+          0.000000 0.054001 0.000000
+          0.766667
+          0.026119 0.038580 -0.011710 0.998845
+          0.000000 0.054001 0.000000
+          0.800000
+          0.029294 0.023442 -0.007318 0.999269
+          0.000000 0.054001 0.000000
+          0.833334
+          0.028076 0.001956 -0.000087 0.999604
+          0.000000 0.054001 0.000000
+          0.866667
+          0.026126 -0.023192 0.009273 0.999347
+          0.000000 0.054002 -0.000000
+          0.900000
+          0.023686 -0.037598 0.015618 0.998890
+          0.000000 0.054001 -0.000000
+          0.933334
+          0.021977 -0.042971 0.022452 0.998582
+          0.000000 0.054001 -0.000000
+          0.966667
+          0.017826 -0.041019 0.027335 0.998625
+          0.000000 0.054001 -0.000000
+          1.000000
+          0.014652 -0.038091 0.030266 0.998708
+          0.000000 0.054001 -0.000000
+          1.033334
+          0.015140 -0.033696 0.030267 0.998859
+          -0.000000 0.054001 0.000000
+          1.066667
+          0.013307 -0.018802 0.022457 0.999483
+          -0.000000 0.054001 -0.000000
+          1.100000
+          0.011352 -0.001818 0.015137 0.999819
+          0.000000 0.054001 0.000000
+          1.133334
+          0.015013 0.015015 0.004886 0.999763
+          0.000000 0.054001 0.000000
+          1.166667
+          0.010617 0.026613 -0.002435 0.999587
+          0.000000 0.054001 0.000000
+          1.200000
+          0.007809 0.033694 -0.006829 0.999378
+          0.000000 0.054001 -0.000000
+          1.233333
+          0.010616 0.037601 -0.010734 0.999179
+          0.000000 0.054000 0.000000
+          1.266667
+          0.010982 0.037113 -0.010734 0.999193
+          0.000000 0.054002 0.000000
+          1.300000
+          0.009029 0.034183 -0.009270 0.999332
+          0.000000 0.054001 0.000000
+          1.333333
+          0.013670 0.019778 -0.004878 0.999699
+          0.000000 0.054002 0.000000
+          1.366667
+          0.013428 -0.000876 0.001282 0.999909
+          -0.000000 0.054002 -0.000000
+          1.400000
+          0.011966 -0.026612 0.008784 0.999536
+          0.000000 0.054001 0.000000
+          1.433333
+          0.010381 -0.041994 0.013663 0.998971
+          0.000000 0.054000 0.000000
+          1.466667
+          0.009771 -0.047366 0.018545 0.998658
+          0.000000 0.054000 -0.000000
+          1.500000
+          0.007604 -0.044438 0.021475 0.998752
+          0.000000 0.054000 0.000000
+          1.533333
+          0.005436 -0.041508 0.024405 0.998825
+          -0.000000 0.054001 0.000001
+          1.566666
+          0.007816 -0.039067 0.026359 0.998858
+          0.000000 0.054000 0.000000
+          1.600000
+      "C_Head_sjnt_0"
+      49
+          -0.166016 -0.013952 0.076165 0.983078
+          0.000000 0.054000 0.000000
+          0.000000
+          -0.178713 0.002834 0.062008 0.981941
+          0.000000 0.054000 0.000000
+          0.033333
+          -0.199222 0.018032 0.048338 0.978595
+          -0.000000 0.054000 0.000000
+          0.066667
+          -0.176762 0.028782 0.027834 0.983439
+          -0.000000 0.054000 0.000000
+          0.100000
+          -0.204106 0.038059 0.012332 0.978131
+          -0.000000 0.054000 -0.000000
+          0.133333
+          -0.222661 0.041968 -0.012692 0.973909
+          -0.000000 0.054000 -0.000000
+          0.166667
+          -0.201177 0.047833 -0.025630 0.978051
+          0.000000 0.054000 0.000000
+          0.200000
+          -0.182623 0.050764 -0.020502 0.981658
+          -0.000000 0.054000 0.000000
+          0.233333
+          -0.185552 0.047833 -0.016352 0.981333
+          0.000000 0.054000 -0.000000
+          0.266667
+          -0.166997 0.036110 0.012821 0.985213
+          -0.000000 0.054000 0.000000
+          0.300000
+          -0.160159 0.020484 0.023193 0.986606
+          0.000000 0.054000 0.000000
+          0.333333
+          -0.157228 -0.000245 0.041988 0.986669
+          0.000000 0.054000 0.000000
+          0.366667
+          -0.151367 -0.011748 0.054681 0.986894
+          -0.000000 0.054000 0.000000
+          0.400000
+          -0.156250 -0.014680 0.062004 0.985660
+          0.000000 0.054000 0.000000
+          0.433333
+          -0.161133 -0.013460 0.062493 0.984860
+          0.000000 0.054000 0.000000
+          0.466667
+          -0.158204 -0.011019 0.066400 0.985110
+          0.000000 0.054000 0.000000
+          0.500000
+          -0.141602 -0.008821 0.070307 0.987384
+          -0.000000 0.054000 -0.000000
+          0.533333
+          -0.153323 0.007538 0.058103 0.986438
+          -0.000000 0.054000 0.000000
+          0.566667
+          -0.173832 0.023895 0.046387 0.983392
+          -0.000000 0.054000 -0.000000
+          0.600000
+          -0.153325 0.035132 0.027103 0.987179
+          0.000000 0.054000 0.000000
+          0.633333
+          -0.184576 0.043921 0.011357 0.981771
+          0.000000 0.054000 0.000000
+          0.666667
+          -0.205083 0.044900 -0.014156 0.977612
+          -0.000000 0.054000 -0.000000
+          0.700000
+          -0.185552 0.046859 -0.027827 0.981122
+          0.000000 0.054000 0.000000
+          0.733334
+          -0.169927 0.045883 -0.023432 0.984109
+          -0.000000 0.054000 0.000000
+          0.766667
+          -0.175786 0.040999 -0.019771 0.983376
+          0.000000 0.054000 -0.000000
+          0.800000
+          -0.162113 0.029764 0.009402 0.986278
+          0.000000 0.054000 -0.000000
+          0.833334
+          -0.159182 0.013404 0.022460 0.986903
+          -0.000000 0.053999 -0.000000
+          0.866667
+          -0.159180 -0.005766 0.045893 0.986166
+          0.000000 0.053999 0.000000
+          0.900000
+          -0.158203 -0.016145 0.062492 0.985295
+          -0.000000 0.053999 0.000000
+          0.933334
+          -0.166016 -0.018101 0.071281 0.983377
+          0.000000 0.053999 -0.000000
+          0.966667
+          -0.173828 -0.015173 0.072258 0.982004
+          -0.000000 0.054000 -0.000000
+          1.000000
+          -0.173829 -0.011512 0.077141 0.981682
+          -0.000000 0.054001 -0.000000
+          1.033334
+          -0.159181 -0.009069 0.081048 0.983875
+          0.000000 0.054000 0.000000
+          1.066667
+          -0.173831 0.005947 0.065426 0.982582
+          0.000000 0.053999 0.000000
+          1.100000
+          -0.196293 0.019497 0.048827 0.979135
+          0.000000 0.054000 -0.000000
+          1.133334
+          -0.176762 0.026829 0.026124 0.983541
+          -0.000000 0.054000 0.000000
+          1.166667
+          -0.206059 0.033177 0.009035 0.977935
+          -0.000000 0.054000 0.000000
+          1.200000
+          -0.226566 0.036109 -0.016356 0.973189
+          -0.000000 0.053999 0.000000
+          1.233333
+          -0.206059 0.040021 -0.029050 0.977289
+          0.000000 0.054001 0.000000
+          1.266667
+          -0.188481 0.040022 -0.023678 0.980975
+          0.000000 0.054000 0.000001
+          1.300000
+          -0.193363 0.037090 -0.019528 0.980231
+          0.000000 0.054000 0.000000
+          1.333333
+          -0.176761 0.026832 0.010011 0.983837
+          0.000000 0.054001 0.000000
+          1.366667
+          -0.172854 0.011205 0.022703 0.984622
+          0.000000 0.054001 -0.000000
+          1.400000
+          -0.172852 -0.008331 0.044915 0.983888
+          0.000000 0.054001 0.000000
+          1.433333
+          -0.170898 -0.019564 0.059562 0.983292
+          0.000000 0.054000 -0.000000
+          1.466667
+          -0.176757 -0.021031 0.066397 0.981787
+          0.000000 0.053999 0.000000
+          1.500000
+          -0.183593 -0.017126 0.066398 0.980608
+          -0.000000 0.054001 0.000001
+          1.533333
+          -0.182617 -0.014197 0.070305 0.980565
+          0.000000 0.054001 0.000000
+          1.566666
+          -0.166016 -0.013952 0.076164 0.983078
+          -0.000000 0.054002 -0.000000
+          1.600000

+ 5927 - 0
Assets/Human/walk.tof

@@ -0,0 +1,5927 @@
+TOS 1.00
+
+declare SkeletalAnimation 1
+  mAnimatedJoints array instance SkeletalAnimation::AnimatedJoint 
+
+declare SkeletalAnimation::AnimatedJoint 2
+  mJointName string
+  mKeyframes array instance SkeletalAnimation::Keyframe 
+
+declare SkeletalAnimation::Keyframe 3
+  mRotation quat
+  mTranslation vec3
+  mTime float
+
+object SkeletalAnimation 00000001
+  23
+      "hipsBone"
+      85
+          0.028851 0.997984 -0.028764 0.048669
+          -0.020494 0.875553 0.050907
+          0.000000
+          0.023715 0.998435 -0.035948 0.035676
+          -0.017689 0.881168 0.001381
+          0.033333
+          0.027169 0.998487 -0.043596 0.019622
+          -0.015189 0.881899 -0.048877
+          0.066667
+          0.033407 0.998265 -0.048380 0.003199
+          -0.012444 0.877991 -0.100572
+          0.100000
+          0.039450 0.997667 -0.053480 -0.015618
+          -0.009517 0.868466 -0.153610
+          0.133333
+          0.041436 0.996865 -0.056821 -0.036248
+          -0.004973 0.854058 -0.212063
+          0.166667
+          0.033668 0.996505 -0.052663 -0.055413
+          0.001019 0.838919 -0.275641
+          0.200000
+          0.009325 0.996643 -0.049003 -0.064912
+          0.008902 0.827686 -0.342333
+          0.233333
+          -0.012859 0.996817 -0.043825 -0.065348
+          0.015977 0.821826 -0.408657
+          0.266667
+          -0.026282 0.997204 -0.033163 -0.061595
+          0.021347 0.821584 -0.471807
+          0.300000
+          -0.038324 0.997219 -0.024892 -0.058878
+          0.025129 0.826225 -0.532333
+          0.333333
+          -0.053463 0.996459 -0.022579 -0.060834
+          0.027567 0.833795 -0.589686
+          0.366667
+          -0.058964 0.996209 -0.023537 -0.059477
+          0.028299 0.843074 -0.644721
+          0.400000
+          -0.051076 0.996978 -0.025859 -0.052517
+          0.025982 0.853819 -0.698474
+          0.433333
+          -0.039002 0.997998 -0.029296 -0.040246
+          0.022080 0.863831 -0.750153
+          0.466667
+          -0.030665 0.998599 -0.035075 -0.025103
+          0.018300 0.870912 -0.797803
+          0.500000
+          -0.031852 0.998578 -0.041615 -0.009759
+          0.015617 0.872864 -0.845881
+          0.533333
+          -0.037119 0.998142 -0.048085 0.004828
+          0.012811 0.869688 -0.895302
+          0.566667
+          -0.040486 0.997402 -0.056149 0.019933
+          0.008907 0.860896 -0.946676
+          0.600000
+          -0.041695 0.996489 -0.062142 0.037543
+          0.003798 0.848929 -1.001833
+          0.633333
+          -0.037726 0.995910 -0.061736 0.054118
+          -0.001356 0.836963 -1.061750
+          0.666667
+          -0.026591 0.996038 -0.053404 0.065945
+          -0.006585 0.828174 -1.126670
+          0.700000
+          -0.010017 0.996394 -0.042022 0.073027
+          -0.012318 0.825001 -1.193971
+          0.733334
+          0.005781 0.996707 -0.034220 0.073288
+          -0.017931 0.827689 -1.259074
+          0.766667
+          0.020541 0.997007 -0.029686 0.068370
+          -0.021591 0.834038 -1.319845
+          0.800000
+          0.033663 0.996971 -0.025162 0.065438
+          -0.024152 0.842586 -1.377930
+          0.833334
+          0.038108 0.996985 -0.024313 0.063065
+          -0.024762 0.853331 -1.431744
+          0.866667
+          0.035519 0.997361 -0.024862 0.058230
+          -0.023176 0.865541 -1.484154
+          0.900000
+          0.028851 0.997984 -0.028764 0.048669
+          -0.020494 0.875553 -1.534612
+          0.933334
+          0.023694 0.998435 -0.035948 0.035698
+          -0.017689 0.881168 -1.584155
+          0.966667
+          0.027115 0.998487 -0.043597 0.019675
+          -0.015189 0.881899 -1.634430
+          1.000000
+          0.033320 0.998268 -0.048383 0.003113
+          -0.012444 0.877991 -1.686120
+          1.033334
+          0.039450 0.997667 -0.053480 -0.015618
+          -0.009517 0.868466 -1.739153
+          1.066667
+          0.041436 0.996865 -0.056821 -0.036248
+          -0.004973 0.854058 -1.797605
+          1.100000
+          0.033668 0.996505 -0.052663 -0.055413
+          0.001019 0.838919 -1.861182
+          1.133334
+          0.009325 0.996643 -0.049003 -0.064912
+          0.008902 0.827686 -1.927872
+          1.166667
+          -0.012859 0.996817 -0.043825 -0.065348
+          0.015977 0.821826 -1.994195
+          1.200000
+          -0.026282 0.997204 -0.033163 -0.061595
+          0.021347 0.821584 -2.057344
+          1.233333
+          -0.038324 0.997219 -0.024892 -0.058878
+          0.025129 0.826225 -2.117868
+          1.266667
+          -0.053463 0.996459 -0.022579 -0.060834
+          0.027567 0.833795 -2.175220
+          1.300000
+          -0.058964 0.996209 -0.023537 -0.059476
+          0.028299 0.843074 -2.230253
+          1.333333
+          -0.051076 0.996978 -0.025859 -0.052517
+          0.025982 0.853819 -2.284006
+          1.366667
+          -0.039002 0.997998 -0.029296 -0.040246
+          0.022080 0.863831 -2.335683
+          1.400000
+          -0.030665 0.998599 -0.035075 -0.025103
+          0.018300 0.870912 -2.383332
+          1.433333
+          -0.031852 0.998578 -0.041615 -0.009759
+          0.015617 0.872864 -2.431409
+          1.466667
+          -0.037119 0.998142 -0.048085 0.004828
+          0.012811 0.869688 -2.480828
+          1.500000
+          -0.040486 0.997402 -0.056149 0.019933
+          0.008907 0.860896 -2.532200
+          1.533333
+          -0.041695 0.996489 -0.062142 0.037543
+          0.003798 0.848929 -2.587357
+          1.566666
+          -0.037726 0.995910 -0.061736 0.054118
+          -0.001355 0.836963 -2.647272
+          1.600000
+          -0.026591 0.996038 -0.053405 0.065946
+          -0.006585 0.828174 -2.712191
+          1.633333
+          -0.010017 0.996394 -0.042022 0.073027
+          -0.012318 0.825001 -2.779490
+          1.666666
+          0.005780 0.996707 -0.034220 0.073289
+          -0.017931 0.827689 -2.844592
+          1.700000
+          0.020541 0.997007 -0.029686 0.068370
+          -0.021591 0.834038 -2.905361
+          1.733333
+          0.033662 0.996971 -0.025162 0.065438
+          -0.024152 0.842586 -2.963445
+          1.766666
+          0.038108 0.996985 -0.024313 0.063065
+          -0.024762 0.853331 -3.017258
+          1.800000
+          0.035519 0.997361 -0.024862 0.058230
+          -0.023177 0.865541 -3.069667
+          1.833333
+          0.028851 0.997984 -0.028764 0.048669
+          -0.020494 0.875552 -3.120123
+          1.866666
+          0.023694 0.998435 -0.035948 0.035698
+          -0.017689 0.881168 -3.169665
+          1.899999
+          0.027115 0.998487 -0.043596 0.019676
+          -0.015189 0.881899 -3.219939
+          1.933333
+          0.033320 0.998268 -0.048383 0.003113
+          -0.012444 0.877991 -3.271636
+          1.966666
+          0.039449 0.997667 -0.053479 -0.015617
+          -0.009517 0.868467 -3.324677
+          1.999999
+          0.041436 0.996865 -0.056821 -0.036248
+          -0.004973 0.854058 -3.383129
+          2.033333
+          0.033668 0.996505 -0.052663 -0.055413
+          0.001019 0.838919 -3.446706
+          2.066666
+          0.009327 0.996643 -0.049003 -0.064912
+          0.008901 0.827686 -3.513396
+          2.099999
+          -0.012858 0.996817 -0.043825 -0.065348
+          0.015977 0.821826 -3.579720
+          2.133333
+          -0.026282 0.997204 -0.033164 -0.061595
+          0.021346 0.821584 -3.642869
+          2.166666
+          -0.038324 0.997219 -0.024892 -0.058878
+          0.025129 0.826224 -3.703394
+          2.199999
+          -0.053463 0.996459 -0.022579 -0.060834
+          0.027567 0.833795 -3.760746
+          2.233333
+          -0.058964 0.996209 -0.023537 -0.059476
+          0.028299 0.843074 -3.815779
+          2.266666
+          -0.051076 0.996978 -0.025859 -0.052517
+          0.025982 0.853818 -3.869532
+          2.299999
+          -0.039003 0.997998 -0.029296 -0.040247
+          0.022080 0.863830 -3.921209
+          2.333333
+          -0.030664 0.998599 -0.035075 -0.025104
+          0.018300 0.870911 -3.968858
+          2.366666
+          -0.031853 0.998578 -0.041615 -0.009760
+          0.015617 0.872864 -4.016935
+          2.399999
+          -0.037118 0.998142 -0.048084 0.004827
+          0.012811 0.869689 -4.066355
+          2.433332
+          -0.040486 0.997402 -0.056149 0.019933
+          0.008907 0.860896 -4.117727
+          2.466666
+          -0.041695 0.996489 -0.062142 0.037543
+          0.003798 0.848929 -4.172884
+          2.499999
+          -0.037725 0.995910 -0.061736 0.054117
+          -0.001355 0.836964 -4.232800
+          2.533332
+          -0.026591 0.996038 -0.053405 0.065946
+          -0.006585 0.828174 -4.297718
+          2.566666
+          -0.010018 0.996394 -0.042022 0.073026
+          -0.012318 0.825001 -4.365017
+          2.599999
+          0.005780 0.996707 -0.034220 0.073289
+          -0.017930 0.827689 -4.430120
+          2.633332
+          0.020541 0.997007 -0.029686 0.068371
+          -0.021591 0.834038 -4.490890
+          2.666666
+          0.033576 0.996980 -0.025170 0.065352
+          -0.024152 0.842586 -4.548974
+          2.699999
+          0.038151 0.996986 -0.024314 0.063022
+          -0.024762 0.853330 -4.602787
+          2.733332
+          0.035606 0.997363 -0.024864 0.058144
+          -0.023177 0.865540 -4.655195
+          2.766665
+          0.028851 0.997984 -0.028764 0.048669
+          -0.020494 0.875552 -4.705652
+          2.799999
+      "R_Leg_sjnt_0"
+      85
+          0.967486 -0.025403 0.007812 0.251526
+          -0.104854 -0.000224 0.000080
+          0.000000
+          0.955380 -0.009772 0.016604 0.294749
+          -0.104854 -0.000224 0.000080
+          0.033333
+          0.945617 -0.006841 0.016605 0.324786
+          -0.104854 -0.000224 0.000080
+          0.066667
+          0.940265 -0.011724 0.006960 0.340171
+          -0.104854 -0.000224 0.000080
+          0.100000
+          0.935783 -0.024422 -0.007569 0.351648
+          -0.104854 -0.000224 0.000080
+          0.133333
+          0.926527 -0.036633 -0.015628 0.374115
+          -0.104854 -0.000224 0.000080
+          0.166667
+          0.918519 -0.035657 -0.009525 0.393651
+          -0.104854 -0.000224 0.000080
+          0.200000
+          0.915591 -0.010746 0.003298 0.401953
+          -0.104854 -0.000224 0.000080
+          0.233333
+          0.921249 0.016122 0.009529 0.388522
+          -0.104854 -0.000224 0.000080
+          0.266667
+          0.929610 0.037617 0.007459 0.366544
+          -0.104854 -0.000224 0.000080
+          0.300000
+          0.939543 0.057646 0.006364 0.337484
+          -0.104854 -0.000224 0.000080
+          0.333333
+          0.949678 0.078163 0.012717 0.303052
+          -0.104854 -0.000224 0.000080
+          0.366667
+          0.959563 0.088909 0.014916 0.266666
+          -0.104854 -0.000224 0.000080
+          0.400000
+          0.969875 0.085002 0.009788 0.228082
+          -0.104854 -0.000224 0.000080
+          0.433333
+          0.978994 0.075233 0.001058 0.189499
+          -0.104854 -0.000224 0.000080
+          0.466667
+          0.985686 0.067419 -0.007671 0.154334
+          -0.104854 -0.000224 0.000080
+          0.500000
+          0.990064 0.067908 -0.013897 0.122344
+          -0.104854 -0.000224 0.000080
+          0.533333
+          0.993202 0.070351 -0.016093 0.091330
+          -0.104854 -0.000224 0.000080
+          0.566667
+          0.995342 0.067908 -0.014385 0.066910
+          -0.104854 -0.000224 0.000080
+          0.600000
+          -0.997308 -0.059603 0.012678 -0.040781
+          -0.104854 -0.000224 0.000080
+          0.633333
+          -0.998889 -0.044459 0.012925 -0.008791
+          -0.104854 -0.000224 0.000080
+          0.666667
+          -0.999476 -0.021009 0.012442 0.021246
+          -0.104854 -0.000224 0.000080
+          0.700000
+          -0.999324 0.008790 0.012448 0.033455
+          -0.104854 -0.000224 0.000080
+          0.733334
+          -0.999348 0.033217 0.009768 0.010256
+          -0.104854 -0.000224 0.000080
+          0.766667
+          0.998053 -0.049827 -0.005499 0.037119
+          -0.104854 -0.000224 0.000080
+          0.800000
+          0.994096 -0.060574 -0.005257 0.089866
+          -0.104854 -0.000224 0.000080
+          0.833334
+          0.987318 -0.059597 -0.006355 0.147009
+          -0.104854 -0.000224 0.000080
+          0.866667
+          0.978664 -0.044942 -0.001531 0.200489
+          -0.104854 -0.000224 0.000080
+          0.900000
+          0.967485 -0.025403 0.007812 0.251527
+          -0.104854 -0.000224 0.000080
+          0.933334
+          0.955389 -0.009772 0.016116 0.294750
+          -0.104854 -0.000224 0.000080
+          0.966667
+          0.945612 -0.006352 0.017093 0.324786
+          -0.104854 -0.000224 0.000080
+          1.000000
+          0.940265 -0.011724 0.006960 0.340171
+          -0.104854 -0.000224 0.000080
+          1.033334
+          0.935782 -0.024422 -0.007691 0.351648
+          -0.104854 -0.000224 0.000080
+          1.066667
+          0.926527 -0.036633 -0.015628 0.374115
+          -0.104854 -0.000224 0.000080
+          1.100000
+          0.918519 -0.035657 -0.009525 0.393651
+          -0.104854 -0.000224 0.000080
+          1.133334
+          0.915591 -0.010745 0.003298 0.401953
+          -0.104854 -0.000224 0.000080
+          1.166667
+          0.921249 0.016122 0.009529 0.388522
+          -0.104854 -0.000224 0.000080
+          1.200000
+          0.929610 0.037617 0.007459 0.366544
+          -0.104854 -0.000224 0.000080
+          1.233333
+          0.939543 0.057646 0.006364 0.337484
+          -0.104854 -0.000224 0.000080
+          1.266667
+          0.949678 0.078163 0.012717 0.303052
+          -0.104854 -0.000224 0.000080
+          1.300000
+          0.959563 0.088909 0.014916 0.266666
+          -0.104854 -0.000224 0.000080
+          1.333333
+          0.969875 0.085002 0.009789 0.228083
+          -0.104854 -0.000224 0.000080
+          1.366667
+          0.978994 0.075233 0.001058 0.189499
+          -0.104854 -0.000224 0.000080
+          1.400000
+          0.985686 0.067419 -0.007671 0.154334
+          -0.104854 -0.000224 0.000080
+          1.433333
+          0.990064 0.067908 -0.013897 0.122344
+          -0.104854 -0.000224 0.000080
+          1.466667
+          0.993202 0.070351 -0.016093 0.091330
+          -0.104854 -0.000224 0.000080
+          1.500000
+          0.995342 0.067908 -0.014385 0.066910
+          -0.104854 -0.000224 0.000080
+          1.533333
+          -0.997308 -0.059603 0.012678 -0.040781
+          -0.104854 -0.000224 0.000080
+          1.566666
+          -0.998889 -0.044459 0.012925 -0.008791
+          -0.104854 -0.000224 0.000080
+          1.600000
+          -0.999476 -0.021010 0.012442 0.021245
+          -0.104854 -0.000224 0.000080
+          1.633333
+          -0.999324 0.008790 0.012448 0.033455
+          -0.104854 -0.000224 0.000080
+          1.666666
+          -0.999348 0.033216 0.009768 0.010257
+          -0.104854 -0.000224 0.000080
+          1.700000
+          0.998053 -0.049827 -0.005499 0.037118
+          -0.104854 -0.000224 0.000080
+          1.733333
+          0.994096 -0.060574 -0.005257 0.089865
+          -0.104854 -0.000224 0.000080
+          1.766666
+          0.987318 -0.059597 -0.006355 0.147008
+          -0.104854 -0.000224 0.000080
+          1.800000
+          0.978664 -0.044942 -0.001531 0.200488
+          -0.104854 -0.000224 0.000080
+          1.833333
+          0.967486 -0.025403 0.007812 0.251525
+          -0.104854 -0.000224 0.000080
+          1.866666
+          0.955389 -0.009772 0.016116 0.294749
+          -0.104854 -0.000223 0.000079
+          1.899999
+          0.945612 -0.006352 0.017093 0.324786
+          -0.104854 -0.000224 0.000080
+          1.933333
+          0.940265 -0.011724 0.006960 0.340171
+          -0.104854 -0.000225 0.000081
+          1.966666
+          0.935782 -0.024422 -0.007691 0.351648
+          -0.104854 -0.000224 0.000080
+          1.999999
+          0.926527 -0.036633 -0.015628 0.374114
+          -0.104854 -0.000224 0.000080
+          2.033333
+          0.918519 -0.035657 -0.009525 0.393650
+          -0.104854 -0.000224 0.000079
+          2.066666
+          0.915591 -0.010746 0.003298 0.401953
+          -0.104854 -0.000224 0.000080
+          2.099999
+          0.921249 0.016121 0.009529 0.388523
+          -0.104854 -0.000224 0.000080
+          2.133333
+          0.929610 0.037616 0.007459 0.366545
+          -0.104854 -0.000224 0.000080
+          2.166666
+          0.939543 0.057646 0.006365 0.337485
+          -0.104854 -0.000224 0.000080
+          2.199999
+          0.949678 0.078162 0.012716 0.303053
+          -0.104854 -0.000224 0.000080
+          2.233333
+          0.959563 0.088909 0.014916 0.266667
+          -0.104854 -0.000224 0.000080
+          2.266666
+          0.969875 0.085002 0.009789 0.228084
+          -0.104854 -0.000224 0.000081
+          2.299999
+          0.978994 0.075234 0.001059 0.189500
+          -0.104854 -0.000224 0.000080
+          2.333333
+          0.985686 0.067419 -0.007671 0.154335
+          -0.104854 -0.000224 0.000080
+          2.366666
+          0.990064 0.067908 -0.013896 0.122345
+          -0.104854 -0.000224 0.000080
+          2.399999
+          0.993202 0.070351 -0.016093 0.091331
+          -0.104854 -0.000224 0.000080
+          2.433332
+          0.995341 0.067908 -0.014385 0.066911
+          -0.104854 -0.000224 0.000080
+          2.466666
+          -0.997308 -0.059603 0.012678 -0.040782
+          -0.104854 -0.000224 0.000080
+          2.499999
+          -0.998889 -0.044459 0.012925 -0.008792
+          -0.104854 -0.000224 0.000080
+          2.533332
+          -0.999476 -0.021010 0.012442 0.021245
+          -0.104854 -0.000224 0.000080
+          2.566666
+          -0.999324 0.008789 0.012448 0.033455
+          -0.104854 -0.000224 0.000080
+          2.599999
+          -0.999348 0.033216 0.009768 0.010257
+          -0.104854 -0.000223 0.000081
+          2.633332
+          0.998053 -0.049826 -0.005499 0.037117
+          -0.104854 -0.000224 0.000081
+          2.666666
+          0.994096 -0.060574 -0.005257 0.089864
+          -0.104854 -0.000224 0.000080
+          2.699999
+          0.987319 -0.059597 -0.006233 0.147007
+          -0.104854 -0.000224 0.000080
+          2.733332
+          0.978664 -0.044942 -0.001561 0.200486
+          -0.104854 -0.000224 0.000080
+          2.766665
+          0.967486 -0.025404 0.007811 0.251524
+          -0.104854 -0.000224 0.000081
+          2.799999
+      "R_Leg_sjnt_1"
+      85
+          -0.616024 0.002423 0.001241 -0.787723
+          -0.000000 0.461809 -0.000000
+          0.000000
+          -0.558867 0.002425 0.000876 -0.829253
+          -0.000000 0.461809 -0.000000
+          0.033333
+          -0.479727 0.000475 -0.000648 -0.877417
+          -0.000000 0.461809 -0.000000
+          0.066667
+          -0.377626 0.000188 -0.001073 -0.925958
+          -0.000000 0.461809 -0.000000
+          0.100000
+          0.277968 -0.001824 0.000704 0.960588
+          -0.000000 0.461809 -0.000000
+          0.133333
+          0.231070 0.000860 0.001193 0.972936
+          -0.000000 0.461808 -0.000000
+          0.166667
+          0.251100 0.000922 0.000705 0.967960
+          -0.000000 0.461809 -0.000000
+          0.200000
+          0.300440 0.004890 0.001317 0.953787
+          -0.000000 0.461809 -0.000000
+          0.233333
+          0.334148 0.003915 0.000646 0.942512
+          -0.000000 0.461809 -0.000000
+          0.266667
+          -0.371275 -0.003672 -0.000402 -0.928515
+          -0.000000 0.461809 0.000000
+          0.300000
+          -0.399609 -0.005870 -0.001441 -0.916666
+          -0.000000 0.461809 -0.000000
+          0.333333
+          -0.404006 -0.006847 -0.001929 -0.914729
+          -0.000000 0.461809 -0.000000
+          0.366667
+          -0.396678 -0.005870 -0.001929 -0.917937
+          -0.000000 0.461809 -0.000000
+          0.400000
+          -0.382511 -0.004893 -0.001807 -0.923936
+          -0.000000 0.461809 -0.000000
+          0.433333
+          -0.361505 -0.004892 -0.001684 -0.932356
+          -0.000000 0.461809 0.000000
+          0.466667
+          -0.338056 -0.004892 -0.001561 -0.941112
+          -0.000000 0.461809 -0.000000
+          0.500000
+          -0.317050 -0.004403 -0.001439 -0.948397
+          -0.000000 0.461809 -0.000000
+          0.533333
+          -0.300928 -0.003914 -0.001438 -0.953638
+          -0.000000 0.461809 -0.000000
+          0.566667
+          -0.298486 -0.002693 -0.001194 -0.954409
+          -0.000000 0.461809 -0.000000
+          0.600000
+          0.300928 0.002937 0.001316 0.953641
+          -0.000000 0.461809 0.000000
+          0.633333
+          0.306791 0.003182 0.001194 0.951771
+          -0.000000 0.461808 -0.000000
+          0.666667
+          0.330240 0.001962 0.000645 0.943895
+          -0.000000 0.461808 -0.000000
+          0.700000
+          0.381046 -0.001454 -0.000941 0.924554
+          -0.000000 0.461809 -0.000000
+          0.733334
+          0.465560 -0.002672 -0.001977 0.885010
+          -0.000000 0.461808 0.000000
+          0.766667
+          -0.550563 0.001082 0.001609 -0.834792
+          -0.000000 0.461808 0.000000
+          0.800000
+          -0.614558 -0.000751 0.000218 -0.788871
+          -0.000000 0.461809 -0.000000
+          0.833334
+          -0.648266 0.004862 0.004414 -0.761385
+          -0.000000 0.461809 0.000000
+          0.866667
+          -0.646800 0.004374 0.003681 -0.762638
+          -0.000000 0.461809 -0.000000
+          0.900000
+          -0.616023 0.000653 -0.000011 -0.787728
+          -0.000000 0.461809 -0.000000
+          0.933334
+          -0.558866 0.001570 0.000296 -0.829256
+          -0.000000 0.461809 -0.000000
+          0.966667
+          -0.479726 0.001817 -0.000007 -0.877416
+          -0.000000 0.461808 0.000000
+          1.000000
+          -0.377625 0.000600 -0.000890 -0.925958
+          -0.000000 0.461809 -0.000000
+          1.033334
+          0.277968 -0.001458 0.000827 0.960589
+          -0.000000 0.461808 -0.000000
+          1.066667
+          0.231070 0.001104 0.001193 0.972936
+          -0.000000 0.461809 -0.000000
+          1.100000
+          0.251100 0.000800 0.000644 0.967961
+          0.000000 0.461808 -0.000000
+          1.133334
+          0.300440 0.004890 0.001317 0.953787
+          -0.000000 0.461809 -0.000000
+          1.166667
+          0.334148 0.003915 0.000646 0.942512
+          -0.000000 0.461809 0.000000
+          1.200000
+          -0.371275 -0.003672 -0.000433 -0.928515
+          -0.000000 0.461809 0.000000
+          1.233333
+          -0.399609 -0.005870 -0.001441 -0.916666
+          -0.000000 0.461809 0.000000
+          1.266667
+          -0.404006 -0.006847 -0.001929 -0.914729
+          -0.000000 0.461809 0.000000
+          1.300000
+          -0.396678 -0.005870 -0.001929 -0.917937
+          -0.000000 0.461809 -0.000000
+          1.333333
+          -0.382511 -0.004893 -0.001807 -0.923936
+          -0.000000 0.461809 -0.000000
+          1.366667
+          -0.361505 -0.004892 -0.001684 -0.932356
+          -0.000000 0.461809 -0.000000
+          1.400000
+          -0.338056 -0.004892 -0.001561 -0.941112
+          -0.000000 0.461808 -0.000000
+          1.433333
+          -0.317050 -0.004403 -0.001439 -0.948397
+          -0.000000 0.461809 0.000000
+          1.466667
+          -0.300928 -0.003914 -0.001438 -0.953638
+          -0.000000 0.461808 0.000000
+          1.500000
+          -0.298486 -0.002693 -0.001194 -0.954409
+          -0.000000 0.461809 0.000000
+          1.533333
+          0.300928 0.002937 0.001316 0.953641
+          -0.000000 0.461808 -0.000000
+          1.566666
+          0.306791 0.003182 0.001194 0.951771
+          -0.000000 0.461809 -0.000000
+          1.600000
+          0.330239 0.001962 0.000645 0.943895
+          -0.000000 0.461809 0.000000
+          1.633333
+          0.381045 -0.001454 -0.000941 0.924555
+          -0.000000 0.461808 -0.000000
+          1.666666
+          0.465559 -0.002672 -0.001977 0.885011
+          -0.000000 0.461809 0.000000
+          1.700000
+          -0.550561 0.001082 0.001609 -0.834792
+          -0.000000 0.461808 0.000000
+          1.733333
+          -0.614557 -0.000751 0.000218 -0.788872
+          -0.000000 0.461809 0.000000
+          1.766666
+          -0.648266 0.004862 0.004414 -0.761386
+          -0.000000 0.461808 0.000000
+          1.800000
+          -0.646801 0.004374 0.003681 -0.762637
+          -0.000000 0.461808 -0.000000
+          1.833333
+          -0.616024 0.000653 -0.000011 -0.787727
+          -0.000000 0.461808 0.000000
+          1.866666
+          -0.558868 0.001570 0.000296 -0.829255
+          -0.000000 0.461809 0.000000
+          1.899999
+          -0.479728 0.001817 -0.000007 -0.877415
+          -0.000000 0.461809 0.000000
+          1.933333
+          -0.377628 0.000600 -0.000890 -0.925957
+          -0.000000 0.461808 -0.000000
+          1.966666
+          0.277971 -0.001458 0.000827 0.960588
+          -0.000000 0.461808 -0.000000
+          1.999999
+          0.231071 0.001104 0.001193 0.972935
+          -0.000000 0.461808 0.000000
+          2.033333
+          0.251099 0.000800 0.000644 0.967961
+          -0.000000 0.461809 -0.000000
+          2.066666
+          0.300439 0.004890 0.001317 0.953788
+          -0.000000 0.461808 -0.000000
+          2.099999
+          0.334147 0.003915 0.000646 0.942513
+          -0.000000 0.461808 -0.000000
+          2.133333
+          -0.371275 -0.003672 -0.000433 -0.928516
+          0.000000 0.461809 -0.000000
+          2.166666
+          -0.399609 -0.005870 -0.001441 -0.916666
+          0.000000 0.461808 0.000000
+          2.199999
+          -0.404006 -0.006847 -0.001929 -0.914729
+          0.000000 0.461808 0.000001
+          2.233333
+          -0.396678 -0.005870 -0.001929 -0.917937
+          -0.000000 0.461808 -0.000001
+          2.266666
+          -0.382512 -0.004893 -0.001807 -0.923936
+          -0.000000 0.461808 0.000000
+          2.299999
+          -0.361506 -0.004892 -0.001684 -0.932355
+          -0.000000 0.461809 0.000000
+          2.333333
+          -0.338057 -0.004892 -0.001561 -0.941112
+          -0.000000 0.461808 0.000000
+          2.366666
+          -0.317050 -0.004403 -0.001439 -0.948397
+          -0.000000 0.461808 -0.000001
+          2.399999
+          -0.300929 -0.003914 -0.001439 -0.953637
+          -0.000000 0.461809 -0.000000
+          2.433332
+          -0.298486 -0.002693 -0.001194 -0.954409
+          -0.000000 0.461808 0.000000
+          2.466666
+          0.300928 0.002937 0.001316 0.953641
+          -0.000000 0.461808 -0.000000
+          2.499999
+          0.306790 0.003182 0.001194 0.951771
+          -0.000000 0.461809 0.000000
+          2.533332
+          0.330239 0.001962 0.000645 0.943895
+          -0.000000 0.461808 -0.000000
+          2.566666
+          0.381044 -0.001454 -0.001002 0.924555
+          -0.000000 0.461808 0.000000
+          2.599999
+          0.465557 -0.002428 -0.001977 0.885012
+          -0.000000 0.461808 0.000000
+          2.633332
+          -0.550559 0.000960 0.001487 -0.834794
+          -0.000000 0.461809 0.000000
+          2.666666
+          -0.614556 -0.000324 0.000508 -0.788873
+          -0.000000 0.461808 -0.000001
+          2.699999
+          -0.648265 0.005350 0.004902 -0.761380
+          -0.000000 0.461809 0.000000
+          2.733332
+          -0.646801 0.002910 0.002461 -0.762650
+          -0.000000 0.461809 0.000000
+          2.766665
+          -0.616025 0.002423 0.001241 -0.787722
+          -0.000000 0.461809 -0.000001
+          2.799999
+      "R_Foot_sjnt_0"
+      85
+          0.548533 -0.005112 -0.045408 -0.834879
+          0.000000 0.388174 0.000000
+          0.000000
+          0.557744 -0.040998 -0.056646 -0.827062
+          0.000000 0.388174 0.000000
+          0.033333
+          0.558660 -0.060040 -0.059091 -0.825107
+          0.000000 0.388174 0.000000
+          0.066667
+          0.557357 -0.061505 -0.056162 -0.826085
+          0.000000 0.388174 0.000000
+          0.100000
+          -0.565417 0.059067 0.040536 0.821688
+          0.000000 0.388174 0.000000
+          0.133333
+          -0.598318 0.046367 0.051763 0.798239
+          0.000000 0.388174 0.000001
+          0.166667
+          -0.629789 0.047831 0.047855 0.773814
+          0.000000 0.388174 0.000000
+          0.200000
+          -0.585815 0.054669 0.050300 0.807033
+          0.000000 0.388174 0.000000
+          0.233333
+          -0.526802 0.057114 0.049815 0.846603
+          0.000000 0.388174 0.000000
+          0.266667
+          0.540073 -0.055648 -0.049814 -0.838298
+          0.000000 0.388174 0.000000
+          0.300000
+          0.584968 -0.055157 -0.051765 -0.807521
+          0.000000 0.388174 0.000000
+          0.333333
+          0.616498 -0.054666 -0.054205 -0.783584
+          0.000000 0.388174 0.000000
+          0.366667
+          0.641176 -0.052711 -0.055668 -0.763554
+          0.000000 0.388174 0.000000
+          0.400000
+          0.660793 -0.048804 -0.055178 -0.746945
+          0.000000 0.388174 0.000000
+          0.433333
+          0.675540 -0.042456 -0.052247 -0.734244
+          0.000000 0.388174 0.000000
+          0.466667
+          0.686791 -0.034155 -0.047850 -0.724473
+          0.000000 0.388174 0.000000
+          0.500000
+          0.695647 -0.025122 -0.042965 -0.716657
+          0.000000 0.388174 0.000000
+          0.533333
+          0.706760 -0.013648 -0.036126 -0.706399
+          0.000000 0.388174 0.000000
+          0.566667
+          0.721688 0.000238 -0.025624 -0.691744
+          0.000000 0.388174 0.000000
+          0.600000
+          0.734780 0.013936 -0.011460 -0.678066
+          0.000000 0.388174 0.000000
+          0.633333
+          0.740406 0.021989 0.010636 -0.671716
+          0.000000 0.388174 0.000000
+          0.666667
+          0.722551 0.027844 0.025651 -0.690280
+          0.000000 0.388174 0.000000
+          0.700000
+          0.672949 0.029793 0.025649 -0.738643
+          0.000000 0.388174 0.000000
+          0.733334
+          0.580328 0.018802 0.021737 -0.813875
+          0.000000 0.388173 0.000000
+          0.766667
+          -0.475004 -0.016358 -0.003513 0.879825
+          0.000000 0.388174 0.000000
+          0.800000
+          -0.453288 -0.022950 0.003841 0.891060
+          0.000000 0.388174 0.000000
+          0.833334
+          -0.502686 -0.036136 0.004081 0.863704
+          0.000000 0.388173 0.000000
+          0.866667
+          0.531706 0.035655 -0.030508 -0.845628
+          0.000000 0.388173 0.000000
+          0.900000
+          0.548666 -0.003312 -0.043943 -0.834880
+          0.000000 0.388174 0.000000
+          0.933334
+          0.557829 -0.040509 -0.056158 -0.827062
+          0.000000 0.388174 0.000000
+          0.966667
+          0.558503 -0.060528 -0.060068 -0.825107
+          0.000000 0.388173 0.000000
+          1.000000
+          0.557254 -0.061994 -0.056650 -0.826084
+          0.000000 0.388174 0.000000
+          1.033334
+          -0.565468 0.058578 0.040536 0.821688
+          0.000000 0.388174 -0.000000
+          1.066667
+          -0.598398 0.045879 0.051274 0.798239
+          0.000000 0.388174 0.000000
+          1.100000
+          -0.629789 0.047831 0.047855 0.773814
+          0.000000 0.388174 0.000000
+          1.133334
+          -0.585815 0.054669 0.050300 0.807033
+          0.000000 0.388174 0.000001
+          1.166667
+          -0.526802 0.057114 0.049815 0.846602
+          0.000000 0.388174 0.000000
+          1.200000
+          0.540073 -0.055648 -0.049814 -0.838298
+          0.000000 0.388174 0.000000
+          1.233333
+          0.584968 -0.055157 -0.051765 -0.807521
+          0.000000 0.388174 0.000000
+          1.266667
+          0.616498 -0.054666 -0.054205 -0.783584
+          0.000000 0.388174 0.000000
+          1.300000
+          0.641176 -0.052711 -0.055668 -0.763554
+          0.000000 0.388174 0.000000
+          1.333333
+          0.660793 -0.048804 -0.055178 -0.746945
+          0.000000 0.388174 0.000000
+          1.366667
+          0.675540 -0.042456 -0.052247 -0.734244
+          0.000000 0.388174 0.000000
+          1.400000
+          0.686791 -0.034155 -0.047850 -0.724473
+          0.000000 0.388173 0.000000
+          1.433333
+          0.695647 -0.025122 -0.042965 -0.716657
+          0.000000 0.388174 0.000000
+          1.466667
+          0.706760 -0.013648 -0.036126 -0.706399
+          0.000000 0.388174 0.000000
+          1.500000
+          0.721688 0.000238 -0.025624 -0.691744
+          0.000000 0.388174 0.000000
+          1.533333
+          0.734779 0.013936 -0.011461 -0.678066
+          0.000000 0.388174 0.000000
+          1.566666
+          0.740406 0.021989 0.010636 -0.671715
+          0.000000 0.388174 0.000000
+          1.600000
+          0.722552 0.027844 0.025651 -0.690279
+          0.000000 0.388173 0.000000
+          1.633333
+          0.672950 0.029793 0.025649 -0.738643
+          0.000000 0.388174 0.000000
+          1.666666
+          0.580330 0.018802 0.021737 -0.813874
+          0.000000 0.388174 0.000000
+          1.700000
+          -0.475006 -0.016358 -0.003513 0.879824
+          0.000000 0.388174 0.000000
+          1.733333
+          -0.453288 -0.022950 0.003841 0.891060
+          0.000000 0.388174 0.000000
+          1.766666
+          -0.502685 -0.036136 0.004081 0.863704
+          0.000000 0.388174 0.000000
+          1.800000
+          0.531705 0.035655 -0.030507 -0.845629
+          0.000000 0.388174 0.000000
+          1.833333
+          0.548665 -0.003311 -0.043943 -0.834880
+          0.000000 0.388174 0.000000
+          1.866666
+          0.557829 -0.040509 -0.056157 -0.827062
+          0.000000 0.388173 0.000000
+          1.899999
+          0.558503 -0.060528 -0.060068 -0.825108
+          0.000000 0.388173 0.000000
+          1.933333
+          0.557254 -0.061994 -0.056650 -0.826085
+          0.000000 0.388175 0.000000
+          1.966666
+          -0.565468 0.058579 0.040536 0.821688
+          0.000000 0.388174 0.000001
+          1.999999
+          -0.598397 0.045879 0.051274 0.798240
+          0.000000 0.388174 0.000000
+          2.033333
+          -0.629789 0.047831 0.047855 0.773814
+          0.000000 0.388174 0.000001
+          2.066666
+          -0.585816 0.054669 0.050300 0.807032
+          0.000000 0.388173 0.000000
+          2.099999
+          -0.526803 0.057114 0.049815 0.846602
+          0.000000 0.388174 0.000001
+          2.133333
+          0.540072 -0.055649 -0.049814 -0.838298
+          0.000000 0.388174 0.000000
+          2.166666
+          0.584967 -0.055157 -0.051765 -0.807522
+          0.000000 0.388174 0.000000
+          2.199999
+          0.616497 -0.054666 -0.054205 -0.783584
+          0.000000 0.388174 -0.000000
+          2.233333
+          0.641176 -0.052711 -0.055668 -0.763555
+          0.000000 0.388174 0.000000
+          2.266666
+          0.660792 -0.048804 -0.055178 -0.746945
+          0.000000 0.388174 0.000000
+          2.299999
+          0.675540 -0.042456 -0.052247 -0.734244
+          0.000000 0.388174 0.000000
+          2.333333
+          0.686791 -0.034155 -0.047850 -0.724474
+          0.000000 0.388174 0.000000
+          2.366666
+          0.695647 -0.025122 -0.042965 -0.716658
+          0.000000 0.388174 0.000000
+          2.399999
+          0.706759 -0.013649 -0.036126 -0.706399
+          0.000000 0.388174 0.000000
+          2.433332
+          0.721688 0.000243 -0.025624 -0.691744
+          0.000000 0.388174 0.000000
+          2.466666
+          0.734779 0.013936 -0.011461 -0.678066
+          0.000000 0.388174 0.000000
+          2.499999
+          0.740406 0.021989 0.010635 -0.671716
+          0.000000 0.388174 0.000000
+          2.533332
+          0.722552 0.027844 0.025651 -0.690279
+          0.000000 0.388174 0.000001
+          2.566666
+          0.672951 0.029793 0.025649 -0.738641
+          0.000000 0.388174 0.000001
+          2.599999
+          0.580323 0.018802 0.021981 -0.813872
+          0.000000 0.388174 0.000000
+          2.633332
+          -0.474999 -0.016602 -0.003605 0.879822
+          0.000000 0.388174 0.000000
+          2.666666
+          -0.453311 -0.022461 0.004085 0.891060
+          0.000000 0.388174 0.000000
+          2.699999
+          -0.502715 -0.035648 0.004508 0.863705
+          0.000000 0.388175 0.000000
+          2.733332
+          0.531659 0.037120 -0.029530 -0.845629
+          0.000000 0.388175 0.000000
+          2.766665
+          0.548532 -0.005110 -0.045408 -0.834880
+          0.000000 0.388174 0.000000
+          2.799999
+      "L_Leg_sjnt_0"
+      85
+          -0.172471 0.007179 -0.059550 0.983187
+          0.104854 -0.000225 0.000080
+          0.000000
+          -0.139739 -0.000819 -0.055653 0.988623
+          0.104854 -0.000225 0.000080
+          0.033333
+          -0.109452 -0.009425 -0.059572 0.992161
+          0.104854 -0.000225 0.000080
+          0.066667
+          -0.079165 -0.015406 -0.064467 0.994656
+          0.104854 -0.000225 0.000080
+          0.100000
+          -0.053274 -0.020533 -0.066430 0.996156
+          0.104854 -0.000225 0.000080
+          0.133333
+          -0.024939 -0.025174 -0.060576 0.997534
+          0.104854 -0.000225 0.000080
+          0.166667
+          0.012195 -0.028108 -0.040070 0.998727
+          0.104854 -0.000225 0.000080
+          0.200000
+          0.038586 -0.023478 -0.001972 0.998977
+          0.104854 -0.000225 0.000080
+          0.233333
+          0.034687 -0.019579 0.031738 0.998702
+          0.104854 -0.000225 0.000080
+          0.266667
+          0.006847 -0.024954 0.052263 0.998298
+          0.104854 -0.000225 0.000080
+          0.300000
+          -0.036139 -0.035455 0.064974 0.996602
+          0.104854 -0.000225 0.000080
+          0.333333
+          -0.095246 -0.049128 0.077201 0.991239
+          0.104854 -0.000225 0.000080
+          0.366667
+          -0.156799 -0.054009 0.077217 0.983125
+          0.104854 -0.000225 0.000080
+          0.400000
+          -0.215427 -0.047166 0.060137 0.973525
+          0.104854 -0.000225 0.000080
+          0.433333
+          -0.267704 -0.033975 0.040126 0.962066
+          0.104854 -0.000225 0.000080
+          0.466667
+          -0.312651 -0.022006 0.027928 0.949202
+          0.104854 -0.000225 0.000080
+          0.500000
+          -0.345381 -0.017364 0.029893 0.937826
+          0.104854 -0.000225 0.000080
+          0.533333
+          -0.365408 -0.021026 0.038691 0.929805
+          0.104854 -0.000225 0.000080
+          0.566667
+          -0.382991 -0.027130 0.051885 0.921895
+          0.104854 -0.000225 0.000080
+          0.600000
+          -0.399110 -0.025909 0.058729 0.914653
+          0.104854 -0.000225 0.000080
+          0.633333
+          -0.406928 -0.015653 0.053848 0.911737
+          0.104854 -0.000225 0.000080
+          0.666667
+          -0.408887 -0.005579 0.036264 0.911847
+          0.104854 -0.000225 0.000080
+          0.700000
+          -0.390330 -0.002525 0.013788 0.920568
+          0.104854 -0.000225 0.000080
+          0.733334
+          -0.364933 -0.008199 -0.009670 0.930947
+          0.104854 -0.000225 0.000080
+          0.766667
+          -0.334163 -0.012835 -0.033128 0.941845
+          0.104854 -0.000225 0.000080
+          0.800000
+          -0.296064 -0.007707 -0.053656 0.953629
+          0.104854 -0.000225 0.000080
+          0.833334
+          -0.255520 0.001020 -0.063436 0.964720
+          0.104854 -0.000225 0.000080
+          0.866667
+          -0.212042 0.006999 -0.063936 0.975142
+          0.104854 -0.000225 0.000080
+          0.900000
+          -0.172471 0.006019 -0.059550 0.983194
+          0.104854 -0.000225 0.000080
+          0.933334
+          -0.139739 -0.000390 -0.055653 0.988623
+          0.104854 -0.000225 0.000080
+          0.966667
+          -0.109452 -0.009181 -0.059572 0.992163
+          0.104854 -0.000225 0.000080
+          1.000000
+          -0.079165 -0.015528 -0.064467 0.994654
+          0.104854 -0.000225 0.000080
+          1.033334
+          -0.053274 -0.020777 -0.066430 0.996151
+          0.104854 -0.000225 0.000080
+          1.066667
+          -0.024938 -0.025174 -0.060576 0.997534
+          0.104854 -0.000225 0.000080
+          1.100000
+          0.012195 -0.028108 -0.040070 0.998727
+          0.104854 -0.000225 0.000080
+          1.133334
+          0.038586 -0.023478 -0.001972 0.998977
+          0.104854 -0.000225 0.000080
+          1.166667
+          0.034687 -0.019579 0.031738 0.998702
+          0.104854 -0.000225 0.000080
+          1.200000
+          0.006847 -0.024954 0.052263 0.998298
+          0.104854 -0.000225 0.000080
+          1.233333
+          -0.036139 -0.035455 0.064974 0.996602
+          0.104854 -0.000225 0.000080
+          1.266667
+          -0.095246 -0.049128 0.077201 0.991239
+          0.104854 -0.000225 0.000080
+          1.300000
+          -0.156800 -0.054009 0.077217 0.983125
+          0.104854 -0.000225 0.000080
+          1.333333
+          -0.215426 -0.047166 0.060138 0.973525
+          0.104854 -0.000225 0.000080
+          1.366667
+          -0.267704 -0.033975 0.040126 0.962066
+          0.104854 -0.000225 0.000080
+          1.400000
+          -0.312651 -0.022006 0.027929 0.949202
+          0.104854 -0.000225 0.000080
+          1.433333
+          -0.345381 -0.017364 0.029893 0.937826
+          0.104854 -0.000225 0.000080
+          1.466667
+          -0.365408 -0.021026 0.038691 0.929805
+          0.104854 -0.000225 0.000080
+          1.500000
+          -0.382991 -0.027130 0.051885 0.921895
+          0.104854 -0.000225 0.000080
+          1.533333
+          -0.399110 -0.025909 0.058729 0.914653
+          0.104854 -0.000225 0.000080
+          1.566666
+          -0.406928 -0.015653 0.053848 0.911737
+          0.104854 -0.000225 0.000080
+          1.600000
+          -0.408887 -0.005579 0.036265 0.911847
+          0.104854 -0.000225 0.000080
+          1.633333
+          -0.390330 -0.002525 0.013788 0.920568
+          0.104854 -0.000225 0.000080
+          1.666666
+          -0.364934 -0.008198 -0.009669 0.930947
+          0.104854 -0.000225 0.000080
+          1.700000
+          -0.334164 -0.012835 -0.033128 0.941845
+          0.104854 -0.000225 0.000080
+          1.733333
+          -0.296065 -0.007707 -0.053656 0.953628
+          0.104854 -0.000225 0.000080
+          1.766666
+          -0.255521 0.001020 -0.063436 0.964720
+          0.104854 -0.000225 0.000080
+          1.800000
+          -0.212043 0.006999 -0.063936 0.975142
+          0.104854 -0.000225 0.000080
+          1.833333
+          -0.172472 0.006019 -0.059550 0.983194
+          0.104854 -0.000225 0.000080
+          1.866666
+          -0.139740 -0.000390 -0.055653 0.988623
+          0.104854 -0.000224 0.000080
+          1.899999
+          -0.109453 -0.009181 -0.059572 0.992163
+          0.104854 -0.000225 0.000081
+          1.933333
+          -0.079166 -0.015528 -0.064467 0.994654
+          0.104854 -0.000226 0.000081
+          1.966666
+          -0.053275 -0.020777 -0.066430 0.996151
+          0.104854 -0.000225 0.000080
+          1.999999
+          -0.024939 -0.025173 -0.060577 0.997534
+          0.104854 -0.000225 0.000080
+          2.033333
+          0.012194 -0.028108 -0.040070 0.998727
+          0.104854 -0.000225 0.000080
+          2.066666
+          0.038585 -0.023478 -0.001973 0.998978
+          0.104854 -0.000225 0.000080
+          2.099999
+          0.034687 -0.019579 0.031737 0.998702
+          0.104854 -0.000225 0.000080
+          2.133333
+          0.006848 -0.024954 0.052262 0.998298
+          0.104854 -0.000225 0.000080
+          2.166666
+          -0.036138 -0.035455 0.064974 0.996602
+          0.104854 -0.000225 0.000080
+          2.199999
+          -0.095245 -0.049128 0.077201 0.991239
+          0.104854 -0.000225 0.000080
+          2.233333
+          -0.156798 -0.054009 0.077217 0.983125
+          0.104854 -0.000225 0.000080
+          2.266666
+          -0.215425 -0.047166 0.060138 0.973525
+          0.104854 -0.000225 0.000080
+          2.299999
+          -0.267702 -0.033975 0.040127 0.962066
+          0.104854 -0.000225 0.000080
+          2.333333
+          -0.312650 -0.022006 0.027929 0.949203
+          0.104854 -0.000226 0.000080
+          2.366666
+          -0.345380 -0.017365 0.029893 0.937826
+          0.104854 -0.000225 0.000080
+          2.399999
+          -0.365408 -0.021026 0.038691 0.929805
+          0.104854 -0.000225 0.000080
+          2.433332
+          -0.382990 -0.027130 0.051885 0.921895
+          0.104854 -0.000226 0.000080
+          2.466666
+          -0.399110 -0.025909 0.058729 0.914653
+          0.104854 -0.000225 0.000079
+          2.499999
+          -0.406927 -0.015532 0.053849 0.911740
+          0.104854 -0.000225 0.000080
+          2.533332
+          -0.408887 -0.005640 0.036265 0.911847
+          0.104854 -0.000225 0.000080
+          2.566666
+          -0.390330 -0.002555 0.013789 0.920568
+          0.104854 -0.000225 0.000080
+          2.599999
+          -0.364934 -0.008198 -0.009669 0.930947
+          0.104854 -0.000225 0.000080
+          2.633332
+          -0.334165 -0.012591 -0.033127 0.941848
+          0.104854 -0.000225 0.000081
+          2.666666
+          -0.296066 -0.007585 -0.053655 0.953629
+          0.104854 -0.000225 0.000080
+          2.699999
+          -0.255522 0.000677 -0.063436 0.964720
+          0.104854 -0.000225 0.000080
+          2.733332
+          -0.212044 0.006388 -0.064424 0.975113
+          0.104854 -0.000225 0.000081
+          2.766665
+          -0.172473 0.007179 -0.059550 0.983186
+          0.104854 -0.000225 0.000081
+          2.799999
+      "L_Leg_sjnt_1"
+      85
+          0.335620 0.033689 0.011539 0.941324
+          0.000000 -0.461807 0.000000
+          0.000000
+          0.314125 0.034177 0.011063 0.948702
+          0.000000 -0.461808 0.000000
+          0.033333
+          0.295561 0.034665 0.010585 0.954636
+          0.000000 -0.461808 0.000000
+          0.066667
+          0.284325 0.035154 0.010592 0.958025
+          0.000000 -0.461808 0.000000
+          0.100000
+          0.283837 0.036619 0.010592 0.958115
+          0.000000 -0.461808 0.000000
+          0.133333
+          0.292630 0.036618 0.011075 0.955460
+          0.000000 -0.461808 0.000000
+          0.166667
+          0.301423 0.035154 0.010582 0.952784
+          0.000000 -0.461808 0.000000
+          0.200000
+          0.326827 0.034665 0.011544 0.944378
+          0.000000 -0.461807 0.000000
+          0.233333
+          0.393755 0.030759 0.012481 0.918616
+          0.000000 -0.461807 0.000000
+          0.266667
+          0.485107 0.020993 0.010474 0.874140
+          0.000000 -0.461808 0.000000
+          0.300000
+          0.567179 0.019528 0.012379 0.823270
+          0.000000 -0.461808 0.000000
+          0.333333
+          0.628243 0.014645 0.011367 0.777796
+          0.000000 -0.461807 0.000000
+          0.366667
+          0.657555 0.014645 0.012815 0.753155
+          0.000000 -0.461807 0.000000
+          0.400000
+          0.655114 0.016842 0.015258 0.755188
+          0.000000 -0.461808 0.000000
+          0.433333
+          0.623849 0.018307 0.015276 0.781181
+          0.000000 -0.461808 0.000000
+          0.466667
+          0.564737 0.018063 0.013357 0.824965
+          0.000000 -0.461808 0.000000
+          0.500000
+          0.484620 0.022946 0.013404 0.874321
+          0.000000 -0.461808 0.000000
+          0.533333
+          0.386427 0.027829 0.012485 0.921816
+          0.000000 -0.461808 0.000000
+          0.566667
+          0.301422 0.024900 0.009115 0.953122
+          0.000000 -0.461808 0.000000
+          0.600000
+          0.254036 0.029295 0.008654 0.966712
+          0.000000 -0.461808 0.000000
+          0.633333
+          0.257455 0.029051 0.008164 0.965819
+          0.000000 -0.461807 0.000000
+          0.666667
+          0.312171 0.033201 0.010575 0.949387
+          0.000000 -0.461808 0.000000
+          0.700000
+          0.354183 0.029050 0.010062 0.934671
+          0.000000 -0.461808 0.000000
+          0.733334
+          0.384960 0.027341 0.010044 0.922474
+          0.000000 -0.461807 0.000000
+          0.766667
+          0.398151 0.028806 0.011013 0.916801
+          0.000000 -0.461807 0.000000
+          0.800000
+          0.397174 0.031003 0.011991 0.917141
+          0.000000 -0.461807 0.000000
+          0.833334
+          0.383984 0.031247 0.011999 0.922733
+          0.000000 -0.461807 0.000000
+          0.866667
+          0.360535 0.032224 0.011524 0.932118
+          0.000000 -0.461808 0.000000
+          0.900000
+          0.335620 0.033200 0.011050 0.941347
+          0.000000 -0.461807 -0.000000
+          0.933334
+          0.314125 0.034177 0.011063 0.948702
+          0.000000 -0.461808 0.000000
+          0.966667
+          0.296049 0.034665 0.010585 0.954485
+          0.000000 -0.461807 0.000000
+          1.000000
+          0.284325 0.035154 0.010592 0.958025
+          0.000000 -0.461808 0.000000
+          1.033334
+          0.283837 0.036619 0.010592 0.958115
+          0.000000 -0.461807 0.000000
+          1.066667
+          0.292630 0.036618 0.011075 0.955460
+          0.000000 -0.461808 0.000000
+          1.100000
+          0.301423 0.035154 0.010582 0.952784
+          0.000000 -0.461808 0.000000
+          1.133334
+          0.326827 0.034665 0.011544 0.944378
+          0.000000 -0.461808 0.000000
+          1.166667
+          0.393755 0.030759 0.012481 0.918616
+          0.000000 -0.461807 0.000000
+          1.200000
+          0.485107 0.020993 0.010474 0.874140
+          0.000000 -0.461807 0.000000
+          1.233333
+          0.567179 0.019528 0.012379 0.823270
+          0.000000 -0.461808 0.000000
+          1.266667
+          0.628243 0.014645 0.011367 0.777796
+          0.000000 -0.461807 0.000000
+          1.300000
+          0.657555 0.014645 0.012815 0.753155
+          0.000000 -0.461808 0.000000
+          1.333333
+          0.655114 0.016842 0.015258 0.755188
+          0.000000 -0.461808 0.000000
+          1.366667
+          0.623849 0.018307 0.015276 0.781181
+          0.000000 -0.461807 0.000000
+          1.400000
+          0.564737 0.018063 0.013357 0.824965
+          0.000000 -0.461808 0.000000
+          1.433333
+          0.484620 0.022946 0.013404 0.874321
+          0.000000 -0.461807 0.000000
+          1.466667
+          0.386427 0.027829 0.012485 0.921815
+          0.000000 -0.461808 0.000000
+          1.500000
+          0.301423 0.024900 0.009115 0.953122
+          0.000000 -0.461807 0.000000
+          1.533333
+          0.254036 0.029295 0.008654 0.966712
+          0.000000 -0.461807 0.000000
+          1.566666
+          0.257455 0.029051 0.008164 0.965819
+          0.000000 -0.461808 0.000000
+          1.600000
+          0.312170 0.033200 0.010575 0.949387
+          0.000000 -0.461808 0.000000
+          1.633333
+          0.354183 0.029050 0.010062 0.934671
+          0.000000 -0.461808 0.000000
+          1.666666
+          0.384959 0.027341 0.010044 0.922474
+          0.000000 -0.461808 0.000000
+          1.700000
+          0.398150 0.028806 0.011013 0.916802
+          0.000000 -0.461808 0.000000
+          1.733333
+          0.397174 0.031003 0.011991 0.917141
+          0.000000 -0.461808 -0.000000
+          1.766666
+          0.383984 0.031247 0.011999 0.922733
+          0.000000 -0.461807 0.000000
+          1.800000
+          0.360535 0.032224 0.011524 0.932118
+          0.000000 -0.461807 0.000000
+          1.833333
+          0.335620 0.033200 0.011050 0.941347
+          0.000000 -0.461808 0.000000
+          1.866666
+          0.314126 0.034177 0.011063 0.948702
+          0.000000 -0.461808 0.000000
+          1.899999
+          0.296050 0.034665 0.010585 0.954485
+          0.000000 -0.461808 0.000000
+          1.933333
+          0.284325 0.035154 0.010592 0.958025
+          0.000000 -0.461807 0.000000
+          1.966666
+          0.283837 0.036619 0.010592 0.958115
+          0.000000 -0.461807 0.000000
+          1.999999
+          0.292630 0.036618 0.011075 0.955460
+          0.000000 -0.461808 0.000000
+          2.033333
+          0.301423 0.035154 0.010582 0.952784
+          0.000000 -0.461807 0.000000
+          2.066666
+          0.326826 0.034665 0.011544 0.944378
+          0.000000 -0.461807 0.000000
+          2.099999
+          0.393753 0.030759 0.012481 0.918617
+          0.000000 -0.461808 0.000000
+          2.133333
+          0.485105 0.020994 0.010474 0.874141
+          0.000000 -0.461808 0.000000
+          2.166666
+          0.567177 0.019528 0.012379 0.823271
+          0.000000 -0.461807 0.000000
+          2.199999
+          0.628242 0.014646 0.011367 0.777797
+          0.000000 -0.461808 0.000000
+          2.233333
+          0.657555 0.014645 0.012815 0.753155
+          0.000000 -0.461808 0.000000
+          2.266666
+          0.655114 0.016842 0.015258 0.755188
+          0.000000 -0.461807 0.000000
+          2.299999
+          0.623850 0.018307 0.015276 0.781181
+          0.000000 -0.461808 0.000000
+          2.333333
+          0.564739 0.018063 0.013357 0.824964
+          0.000000 -0.461807 0.000000
+          2.366666
+          0.484622 0.022946 0.013404 0.874320
+          0.000000 -0.461807 0.000000
+          2.399999
+          0.386430 0.027829 0.012485 0.921814
+          0.000000 -0.461807 0.000000
+          2.433332
+          0.301425 0.024900 0.009115 0.953121
+          0.000000 -0.461808 0.000000
+          2.466666
+          0.254037 0.029295 0.008655 0.966712
+          0.000000 -0.461807 0.000000
+          2.499999
+          0.257455 0.029051 0.008164 0.965819
+          0.000000 -0.461807 0.000000
+          2.533332
+          0.312169 0.033200 0.010575 0.949387
+          0.000000 -0.461808 0.000001
+          2.566666
+          0.354181 0.029050 0.010062 0.934671
+          0.000000 -0.461807 0.000000
+          2.599999
+          0.384959 0.027341 0.010044 0.922474
+          0.000000 -0.461808 0.000000
+          2.633332
+          0.398150 0.028806 0.011013 0.916802
+          0.000000 -0.461809 0.000000
+          2.666666
+          0.397174 0.031003 0.011991 0.917141
+          0.000000 -0.461807 0.000000
+          2.699999
+          0.383985 0.031247 0.011999 0.922733
+          0.000000 -0.461807 -0.000001
+          2.733332
+          0.360536 0.032224 0.011524 0.932117
+          0.000000 -0.461808 0.000000
+          2.766665
+          0.335621 0.033689 0.011539 0.941324
+          0.000000 -0.461807 0.000000
+          2.799999
+      "L_Foot_sjnt_0"
+      85
+          -0.664857 -0.002130 0.017891 0.746753
+          -0.000001 -0.388173 -0.000000
+          0.000000
+          -0.674139 -0.006467 0.016187 0.738399
+          -0.000001 -0.388173 -0.000000
+          0.033333
+          -0.681466 -0.011048 0.014482 0.731623
+          -0.000001 -0.388173 -0.000000
+          0.066667
+          -0.689282 -0.017642 0.010701 0.724199
+          -0.000001 -0.388173 -0.000000
+          0.100000
+          -0.702473 -0.027900 0.003750 0.711154
+          -0.000001 -0.388173 -0.000000
+          0.133333
+          -0.716640 -0.043529 -0.005948 0.696058
+          -0.000001 -0.388173 -0.000000
+          0.166667
+          -0.722501 -0.061596 -0.015530 0.688445
+          -0.000001 -0.388173 -0.000000
+          0.200000
+          -0.706382 -0.073306 -0.028483 0.703448
+          -0.000001 -0.388173 -0.000000
+          0.233333
+          -0.636533 -0.071319 -0.051721 0.766201
+          -0.000001 -0.388173 -0.000000
+          0.266667
+          -0.521253 -0.061504 -0.076201 0.847765
+          -0.000001 -0.388173 -0.000000
+          0.300000
+          -0.438698 -0.052196 -0.083081 0.893262
+          -0.000001 -0.388173 -0.000000
+          0.333333
+          -0.440642 -0.062454 -0.065504 0.893109
+          -0.000001 -0.388173 -0.000000
+          0.366667
+          -0.481183 -0.058078 -0.046438 0.873460
+          -0.000001 -0.388173 -0.000000
+          0.400000
+          -0.519283 -0.035147 -0.019557 0.853655
+          -0.000001 -0.388173 -0.000000
+          0.433333
+          -0.545664 -0.000095 0.002682 0.838000
+          -0.000001 -0.388173 -0.000000
+          0.466667
+          -0.548594 0.026112 0.022953 0.835366
+          -0.000001 -0.388173 -0.000000
+          0.500000
+          -0.536381 0.040519 0.031250 0.842423
+          -0.000001 -0.388173 -0.000000
+          0.533333
+          -0.527584 0.032221 0.034661 0.848184
+          -0.000001 -0.388173 -0.000000
+          0.566667
+          -0.548594 0.020010 0.017336 0.835669
+          -0.000001 -0.388173 -0.000000
+          0.600000
+          -0.572539 0.022202 0.000604 0.819576
+          -0.000001 -0.388173 -0.000000
+          0.633333
+          -0.598437 0.029762 -0.008023 0.800577
+          -0.000001 -0.388173 -0.000000
+          0.666667
+          -0.577421 0.031230 0.013694 0.815734
+          -0.000001 -0.388173 -0.000000
+          0.700000
+          -0.550551 0.033681 0.018561 0.833915
+          -0.000001 -0.388173 -0.000000
+          0.733334
+          -0.567650 0.034651 0.018816 0.822325
+          -0.000001 -0.388173 -0.000000
+          0.766667
+          -0.593053 0.033176 0.019319 0.804248
+          -0.000001 -0.388173 -0.000000
+          0.800000
+          -0.617966 0.028771 0.021530 0.785383
+          -0.000001 -0.388173 -0.000000
+          0.833334
+          -0.639458 0.019729 0.023006 0.768229
+          -0.000001 -0.388173 -0.000000
+          0.866667
+          -0.654111 0.008737 0.021304 0.756048
+          -0.000001 -0.388173 -0.000000
+          0.900000
+          -0.664857 -0.000956 0.018379 0.746744
+          -0.000001 -0.388173 -0.000000
+          0.933334
+          -0.674139 -0.006895 0.015943 0.738400
+          -0.000001 -0.388173 -0.000000
+          0.966667
+          -0.681466 -0.011292 0.014237 0.731624
+          -0.000001 -0.388173 -0.000000
+          1.000000
+          -0.689282 -0.017642 0.010701 0.724199
+          -0.000001 -0.388173 -0.000000
+          1.033334
+          -0.702473 -0.027900 0.003811 0.711153
+          -0.000001 -0.388173 -0.000000
+          1.066667
+          -0.716640 -0.043529 -0.005948 0.696058
+          -0.000001 -0.388173 -0.000000
+          1.100000
+          -0.722501 -0.061596 -0.015774 0.688439
+          -0.000001 -0.388173 -0.000000
+          1.133334
+          -0.706382 -0.073306 -0.028483 0.703448
+          -0.000001 -0.388173 -0.000000
+          1.166667
+          -0.636532 -0.071319 -0.051721 0.766202
+          -0.000001 -0.388173 -0.000000
+          1.200000
+          -0.521253 -0.061504 -0.076201 0.847765
+          -0.000001 -0.388173 -0.000000
+          1.233333
+          -0.438698 -0.052196 -0.083081 0.893262
+          -0.000001 -0.388173 -0.000000
+          1.266667
+          -0.440642 -0.062454 -0.065504 0.893109
+          -0.000001 -0.388172 -0.000000
+          1.300000
+          -0.481183 -0.058078 -0.046438 0.873460
+          -0.000001 -0.388173 -0.000000
+          1.333333
+          -0.519283 -0.035147 -0.019557 0.853655
+          -0.000001 -0.388173 -0.000000
+          1.366667
+          -0.545664 -0.000095 0.002682 0.838000
+          -0.000001 -0.388173 -0.000000
+          1.400000
+          -0.548593 0.026112 0.022953 0.835366
+          -0.000001 -0.388173 -0.000001
+          1.433333
+          -0.536381 0.040519 0.031250 0.842423
+          -0.000001 -0.388173 0.000000
+          1.466667
+          -0.527584 0.032221 0.034661 0.848184
+          -0.000001 -0.388173 -0.000000
+          1.500000
+          -0.548594 0.020010 0.017336 0.835669
+          -0.000001 -0.388172 -0.000000
+          1.533333
+          -0.572539 0.022202 0.000604 0.819576
+          -0.000001 -0.388173 -0.000000
+          1.566666
+          -0.598436 0.029762 -0.008023 0.800577
+          -0.000001 -0.388173 -0.000000
+          1.600000
+          -0.577422 0.031230 0.013693 0.815733
+          -0.000001 -0.388173 0.000000
+          1.633333
+          -0.550552 0.033681 0.018561 0.833915
+          -0.000001 -0.388172 -0.000001
+          1.666666
+          -0.567650 0.034651 0.018816 0.822325
+          -0.000001 -0.388173 0.000000
+          1.700000
+          -0.593052 0.033176 0.019319 0.804248
+          -0.000001 -0.388173 -0.000000
+          1.733333
+          -0.617965 0.028771 0.021530 0.785384
+          -0.000001 -0.388172 -0.000000
+          1.766666
+          -0.639457 0.019730 0.023006 0.768229
+          -0.000001 -0.388173 -0.000000
+          1.800000
+          -0.654111 0.008738 0.021304 0.756048
+          -0.000001 -0.388173 -0.000001
+          1.833333
+          -0.664857 -0.000955 0.018379 0.746744
+          -0.000001 -0.388172 0.000000
+          1.866666
+          -0.674138 -0.006894 0.015943 0.738401
+          -0.000001 -0.388173 0.000000
+          1.899999
+          -0.681466 -0.011292 0.014237 0.731624
+          -0.000001 -0.388173 0.000000
+          1.933333
+          -0.689282 -0.017642 0.010701 0.724199
+          -0.000001 -0.388173 -0.000001
+          1.966666
+          -0.702472 -0.027900 0.003811 0.711154
+          -0.000001 -0.388173 0.000000
+          1.999999
+          -0.716639 -0.043529 -0.005948 0.696059
+          -0.000001 -0.388173 -0.000000
+          2.033333
+          -0.722501 -0.061596 -0.015774 0.688439
+          -0.000001 -0.388173 -0.000000
+          2.066666
+          -0.706382 -0.073305 -0.028482 0.703448
+          -0.000001 -0.388173 -0.000001
+          2.099999
+          -0.636534 -0.071319 -0.051720 0.766200
+          -0.000001 -0.388173 -0.000000
+          2.133333
+          -0.521256 -0.061504 -0.076201 0.847763
+          -0.000001 -0.388172 -0.000001
+          2.166666
+          -0.438700 -0.052196 -0.083081 0.893261
+          -0.000001 -0.388173 -0.000000
+          2.199999
+          -0.440642 -0.062454 -0.065504 0.893109
+          -0.000001 -0.388173 -0.000001
+          2.233333
+          -0.481182 -0.058078 -0.046439 0.873461
+          -0.000001 -0.388173 -0.000000
+          2.266666
+          -0.519282 -0.035148 -0.019558 0.853656
+          -0.000001 -0.388173 -0.000000
+          2.299999
+          -0.545663 -0.000096 0.002681 0.838000
+          -0.000001 -0.388173 -0.000000
+          2.333333
+          -0.548594 0.026111 0.022952 0.835366
+          -0.000001 -0.388173 -0.000000
+          2.366666
+          -0.536381 0.040519 0.031250 0.842423
+          -0.000001 -0.388173 -0.000000
+          2.399999
+          -0.527584 0.032221 0.034661 0.848183
+          -0.000001 -0.388172 -0.000000
+          2.433332
+          -0.548594 0.020010 0.017337 0.835670
+          -0.000001 -0.388174 0.000000
+          2.466666
+          -0.572538 0.022202 0.000597 0.819577
+          -0.000001 -0.388173 0.000000
+          2.499999
+          -0.598436 0.029762 -0.008022 0.800577
+          -0.000001 -0.388172 0.000000
+          2.533332
+          -0.577423 0.031230 0.013693 0.815733
+          -0.000001 -0.388173 0.000000
+          2.566666
+          -0.550552 0.033681 0.018805 0.833909
+          -0.000001 -0.388173 -0.000001
+          2.599999
+          -0.567649 0.034651 0.018816 0.822326
+          -0.000001 -0.388172 -0.000000
+          2.633332
+          -0.593052 0.032688 0.019319 0.804269
+          -0.000001 -0.388173 -0.000000
+          2.666666
+          -0.617964 0.028772 0.021530 0.785384
+          -0.000001 -0.388173 -0.000000
+          2.699999
+          -0.639457 0.019974 0.023006 0.768223
+          -0.000001 -0.388173 0.000000
+          2.733332
+          -0.654111 0.009348 0.021549 0.756034
+          -0.000001 -0.388173 0.000000
+          2.766665
+          -0.664857 -0.002130 0.017891 0.746753
+          -0.000001 -0.388173 0.000000
+          2.799999
+      "C_Spine_sjnt_0"
+      85
+          0.028320 -0.056879 -0.028828 0.997563
+          0.000000 0.055442 -0.000000
+          0.000000
+          0.027100 -0.057366 -0.033956 0.997407
+          0.000000 0.055442 0.000000
+          0.033333
+          0.025879 -0.056876 -0.042747 0.997130
+          -0.000000 0.055442 0.000000
+          0.066667
+          0.024902 -0.055897 -0.053248 0.996705
+          -0.000000 0.055442 0.000000
+          0.100000
+          0.024658 -0.055651 -0.062283 0.996201
+          0.000000 0.055442 0.000000
+          0.133333
+          0.024902 -0.056382 -0.065702 0.995934
+          0.000000 0.055442 0.000000
+          0.166667
+          0.025147 -0.058336 -0.062039 0.996050
+          0.000000 0.055442 0.000000
+          0.200000
+          0.025146 -0.060291 -0.052760 0.996468
+          -0.000000 0.055442 -0.000000
+          0.233333
+          0.025391 -0.060782 -0.042260 0.996933
+          0.000000 0.055442 0.000000
+          0.266667
+          0.025635 -0.059807 -0.032980 0.997336
+          0.000000 0.055442 -0.000000
+          0.300000
+          0.025879 -0.057368 -0.025165 0.997700
+          0.000000 0.055442 0.000000
+          0.333333
+          0.026367 -0.054195 -0.017594 0.998027
+          0.000000 0.055442 0.000000
+          0.366667
+          0.026611 -0.051023 -0.011000 0.998282
+          0.000000 0.055442 0.000000
+          0.400000
+          0.027100 -0.048094 -0.005871 0.998458
+          -0.000000 0.055442 0.000000
+          0.433333
+          0.027832 -0.045898 -0.002208 0.998556
+          0.000000 0.055442 0.000000
+          0.466667
+          0.028809 -0.044434 0.002433 0.998594
+          0.000000 0.055442 0.000000
+          0.500000
+          0.029785 -0.044436 0.010003 0.998518
+          0.000000 0.055442 0.000000
+          0.533333
+          0.030274 -0.044438 0.020503 0.998343
+          0.000000 0.055442 -0.000000
+          0.566667
+          0.030029 -0.043464 0.030760 0.998130
+          -0.000000 0.055442 0.000000
+          0.600000
+          0.029541 -0.041268 0.036377 0.998049
+          -0.000000 0.055442 -0.000000
+          0.633333
+          0.029053 -0.038582 0.034912 0.998223
+          0.000000 0.055442 0.000000
+          0.666667
+          0.029053 -0.037115 0.027342 0.998514
+          -0.000000 0.055442 0.000000
+          0.700000
+          0.029053 -0.038090 0.016842 0.998710
+          -0.000000 0.055442 0.000000
+          0.733334
+          0.029297 -0.041261 0.006340 0.998699
+          0.000000 0.055442 0.000000
+          0.766667
+          0.029053 -0.045654 -0.002940 0.998530
+          -0.000000 0.055442 -0.000000
+          0.800000
+          0.029053 -0.050046 -0.011732 0.998255
+          0.000000 0.055442 0.000000
+          0.833334
+          0.028809 -0.053219 -0.018815 0.997990
+          0.000000 0.055442 -0.000000
+          0.866667
+          0.028809 -0.055659 -0.024432 0.997735
+          0.000000 0.055442 0.000000
+          0.900000
+          0.028076 -0.056879 -0.028828 0.997570
+          0.000000 0.055442 0.000000
+          0.933334
+          0.027100 -0.057366 -0.034200 0.997399
+          0.000000 0.055442 0.000000
+          0.966667
+          0.025879 -0.056876 -0.042503 0.997140
+          0.000000 0.055442 0.000000
+          1.000000
+          0.024902 -0.055897 -0.053003 0.996718
+          0.000000 0.055442 0.000000
+          1.033334
+          0.024658 -0.055651 -0.062283 0.996201
+          0.000000 0.055442 0.000000
+          1.066667
+          0.024658 -0.056382 -0.065946 0.995924
+          0.000000 0.055442 0.000000
+          1.100000
+          0.025147 -0.058336 -0.062039 0.996050
+          0.000000 0.055442 0.000000
+          1.133334
+          0.025146 -0.060291 -0.052760 0.996468
+          0.000000 0.055442 0.000000
+          1.166667
+          0.025391 -0.060782 -0.042260 0.996933
+          0.000000 0.055442 -0.000000
+          1.200000
+          0.025635 -0.059807 -0.032980 0.997336
+          -0.000000 0.055442 0.000000
+          1.233333
+          0.025879 -0.057368 -0.025165 0.997700
+          0.000000 0.055442 0.000000
+          1.266667
+          0.026367 -0.054195 -0.017838 0.998023
+          0.000000 0.055442 0.000000
+          1.300000
+          0.026611 -0.051023 -0.011000 0.998282
+          0.000000 0.055442 0.000000
+          1.333333
+          0.027100 -0.048094 -0.005871 0.998458
+          0.000000 0.055442 0.000000
+          1.366667
+          0.027832 -0.045898 -0.002208 0.998556
+          0.000000 0.055442 0.000000
+          1.400000
+          0.028809 -0.044434 0.002432 0.998594
+          0.000000 0.055442 -0.000000
+          1.433333
+          0.029785 -0.044436 0.010003 0.998518
+          0.000000 0.055442 0.000000
+          1.466667
+          0.030274 -0.044438 0.020503 0.998343
+          -0.000000 0.055442 0.000000
+          1.500000
+          0.030029 -0.043464 0.030760 0.998130
+          0.000000 0.055442 0.000000
+          1.533333
+          0.029541 -0.041268 0.036377 0.998049
+          -0.000000 0.055442 -0.000000
+          1.566666
+          0.029053 -0.038582 0.034912 0.998223
+          -0.000000 0.055442 0.000000
+          1.600000
+          0.029053 -0.037115 0.027343 0.998514
+          0.000000 0.055442 -0.000000
+          1.633333
+          0.029053 -0.038090 0.016842 0.998710
+          0.000000 0.055442 -0.000000
+          1.666666
+          0.029297 -0.041261 0.006341 0.998699
+          0.000000 0.055442 0.000000
+          1.700000
+          0.029053 -0.045654 -0.002940 0.998530
+          -0.000000 0.055442 0.000000
+          1.733333
+          0.029053 -0.050046 -0.011732 0.998255
+          -0.000000 0.055442 -0.000000
+          1.766666
+          0.028809 -0.053219 -0.018815 0.997990
+          0.000000 0.055442 0.000000
+          1.800000
+          0.028809 -0.055659 -0.024432 0.997735
+          -0.000000 0.055442 -0.000000
+          1.833333
+          0.028076 -0.056879 -0.028828 0.997570
+          0.000000 0.055442 -0.000000
+          1.866666
+          0.027100 -0.057366 -0.034200 0.997399
+          0.000000 0.055443 -0.000000
+          1.899999
+          0.025879 -0.056876 -0.042503 0.997140
+          0.000000 0.055442 0.000000
+          1.933333
+          0.024902 -0.055897 -0.053003 0.996718
+          0.000000 0.055441 0.000000
+          1.966666
+          0.024658 -0.055651 -0.062283 0.996201
+          -0.000000 0.055442 0.000000
+          1.999999
+          0.024658 -0.056382 -0.065946 0.995924
+          0.000000 0.055442 0.000000
+          2.033333
+          0.025147 -0.058336 -0.062039 0.996050
+          0.000000 0.055442 0.000000
+          2.066666
+          0.025147 -0.060291 -0.052760 0.996468
+          0.000000 0.055442 -0.000000
+          2.099999
+          0.025391 -0.060782 -0.042260 0.996933
+          0.000000 0.055442 0.000000
+          2.133333
+          0.025635 -0.059807 -0.032980 0.997336
+          0.000000 0.055442 0.000000
+          2.166666
+          0.025879 -0.057368 -0.025165 0.997700
+          0.000000 0.055442 -0.000000
+          2.199999
+          0.026367 -0.054195 -0.017838 0.998023
+          -0.000000 0.055442 0.000000
+          2.233333
+          0.026611 -0.051023 -0.011000 0.998282
+          0.000000 0.055442 0.000000
+          2.266666
+          0.027100 -0.048095 -0.005871 0.998458
+          0.000000 0.055441 0.000000
+          2.299999
+          0.027832 -0.045898 -0.002208 0.998556
+          0.000000 0.055442 0.000000
+          2.333333
+          0.028809 -0.044434 0.002432 0.998594
+          0.000000 0.055442 0.000000
+          2.366666
+          0.029785 -0.044436 0.010002 0.998518
+          0.000000 0.055442 0.000000
+          2.399999
+          0.030273 -0.044438 0.020503 0.998343
+          0.000000 0.055442 -0.000000
+          2.433332
+          0.030029 -0.043464 0.030760 0.998130
+          0.000000 0.055442 0.000000
+          2.466666
+          0.029541 -0.041268 0.036377 0.998049
+          -0.000000 0.055442 -0.000000
+          2.499999
+          0.029053 -0.038582 0.034912 0.998223
+          -0.000000 0.055442 -0.000000
+          2.533332
+          0.029053 -0.037115 0.027343 0.998514
+          -0.000000 0.055442 -0.000000
+          2.566666
+          0.029053 -0.038090 0.016842 0.998710
+          0.000000 0.055442 0.000000
+          2.599999
+          0.029297 -0.041261 0.006341 0.998699
+          0.000000 0.055442 0.000000
+          2.633332
+          0.029053 -0.045898 -0.002940 0.998519
+          0.000000 0.055442 0.000000
+          2.666666
+          0.029053 -0.050046 -0.011488 0.998258
+          -0.000000 0.055442 -0.000000
+          2.699999
+          0.028809 -0.053219 -0.018814 0.997990
+          -0.000000 0.055442 0.000000
+          2.733332
+          0.028564 -0.055415 -0.024920 0.997744
+          0.000000 0.055441 0.000000
+          2.766665
+          0.028320 -0.056879 -0.028828 0.997563
+          -0.000000 0.055442 0.000000
+          2.799999
+      "C_Spine_sjnt_1"
+      85
+          0.049805 -0.049314 -0.011973 0.997469
+          -0.000000 0.049000 0.000000
+          0.000000
+          0.048584 -0.050780 -0.007457 0.997500
+          -0.000000 0.049000 0.000000
+          0.033333
+          0.047119 -0.053221 -0.006237 0.997451
+          0.000000 0.049000 -0.000000
+          0.066667
+          0.046631 -0.055418 -0.006787 0.997351
+          -0.000000 0.049000 -0.000000
+          0.100000
+          0.046753 -0.058226 -0.007733 0.997178
+          0.000000 0.049000 0.000000
+          0.133333
+          0.046875 -0.061033 -0.008680 0.996997
+          0.000000 0.049000 0.000000
+          0.166667
+          0.048584 -0.062987 -0.006971 0.996807
+          0.000000 0.049000 0.000000
+          0.200000
+          0.049561 -0.062012 -0.000692 0.996844
+          0.000000 0.049000 0.000000
+          0.233333
+          0.050049 -0.059327 0.006091 0.996965
+          0.000000 0.049000 -0.000000
+          0.266667
+          0.051637 -0.055790 0.011463 0.997041
+          0.000000 0.049000 -0.000000
+          0.300000
+          0.053223 -0.052250 0.016835 0.997073
+          -0.000000 0.049000 0.000000
+          0.333333
+          0.053467 -0.049565 0.021962 0.997097
+          -0.000000 0.049000 0.000000
+          0.366667
+          0.052979 -0.046636 0.022451 0.997253
+          0.000000 0.049000 -0.000000
+          0.400000
+          0.052490 -0.043705 0.018057 0.997501
+          -0.000000 0.049000 0.000000
+          0.433333
+          0.052002 -0.041262 0.011832 0.997724
+          0.000000 0.049000 0.000000
+          0.466667
+          0.051758 -0.039064 0.007011 0.997871
+          0.000000 0.049000 0.000000
+          0.500000
+          0.051270 -0.037110 0.005119 0.997982
+          0.000000 0.049000 0.000000
+          0.533333
+          0.050781 -0.035645 0.004448 0.998064
+          -0.000000 0.049000 0.000000
+          0.566667
+          0.049316 -0.034303 0.003441 0.998188
+          0.000000 0.049000 0.000000
+          0.600000
+          0.047852 -0.032960 0.002434 0.998308
+          0.000000 0.049000 0.000000
+          0.633333
+          0.047607 -0.031983 0.000489 0.998354
+          0.000000 0.049000 0.000000
+          0.666667
+          0.048584 -0.031982 -0.003150 0.998302
+          0.000000 0.049000 0.000000
+          0.700000
+          0.049805 -0.032957 -0.008796 0.998176
+          0.000000 0.049000 0.000000
+          0.733334
+          0.050049 -0.035153 -0.014656 0.998020
+          -0.000000 0.049000 0.000000
+          0.766667
+          0.049316 -0.038570 -0.019784 0.997842
+          0.000000 0.049000 0.000000
+          0.800000
+          0.049072 -0.041987 -0.023202 0.997643
+          0.000000 0.049000 0.000000
+          0.833334
+          0.049316 -0.044673 -0.022226 0.997536
+          0.000000 0.049000 0.000000
+          0.866667
+          0.049805 -0.047115 -0.018076 0.997483
+          -0.000000 0.049000 -0.000000
+          0.900000
+          0.049561 -0.049314 -0.012218 0.997478
+          0.000000 0.049000 0.000000
+          0.933334
+          0.048584 -0.051024 -0.007396 0.997488
+          0.000000 0.049000 0.000000
+          0.966667
+          0.047119 -0.053221 -0.005993 0.997452
+          -0.000000 0.049000 0.000000
+          1.000000
+          0.046875 -0.055663 -0.007031 0.997324
+          -0.000000 0.049000 -0.000000
+          1.033334
+          0.046631 -0.058104 -0.008069 0.997188
+          -0.000000 0.049000 -0.000000
+          1.066667
+          0.046875 -0.061033 -0.008680 0.996997
+          -0.000000 0.049000 0.000000
+          1.100000
+          0.048584 -0.062987 -0.006971 0.996807
+          0.000000 0.049000 0.000000
+          1.133334
+          0.049561 -0.062012 -0.000669 0.996844
+          -0.000000 0.049000 -0.000000
+          1.166667
+          0.050049 -0.059327 0.006091 0.996965
+          0.000000 0.049000 0.000000
+          1.200000
+          0.051637 -0.055790 0.011463 0.997041
+          -0.000000 0.049000 0.000000
+          1.233333
+          0.053223 -0.052250 0.016835 0.997073
+          0.000000 0.049000 0.000000
+          1.266667
+          0.053467 -0.049565 0.021962 0.997097
+          0.000000 0.049000 -0.000000
+          1.300000
+          0.052979 -0.046636 0.022451 0.997253
+          0.000000 0.049000 -0.000000
+          1.333333
+          0.052490 -0.043705 0.018057 0.997501
+          0.000000 0.049000 0.000000
+          1.366667
+          0.052002 -0.041262 0.011832 0.997724
+          0.000000 0.049000 -0.000000
+          1.400000
+          0.051758 -0.039064 0.007011 0.997871
+          0.000000 0.049000 0.000000
+          1.433333
+          0.051270 -0.037110 0.005119 0.997982
+          0.000000 0.049000 -0.000000
+          1.466667
+          0.050781 -0.035645 0.004448 0.998064
+          0.000000 0.049000 -0.000000
+          1.500000
+          0.049317 -0.034303 0.003441 0.998188
+          -0.000000 0.049000 0.000000
+          1.533333
+          0.047851 -0.032960 0.002434 0.998308
+          0.000000 0.049000 0.000000
+          1.566666
+          0.047607 -0.031983 0.000489 0.998354
+          0.000000 0.049000 0.000000
+          1.600000
+          0.048584 -0.031982 -0.003150 0.998302
+          -0.000000 0.049000 -0.000000
+          1.633333
+          0.049805 -0.032957 -0.008796 0.998176
+          -0.000000 0.049000 -0.000000
+          1.666666
+          0.050049 -0.035153 -0.014656 0.998020
+          0.000000 0.049000 0.000000
+          1.700000
+          0.049316 -0.038570 -0.019784 0.997842
+          0.000000 0.049000 0.000000
+          1.733333
+          0.049072 -0.041987 -0.023202 0.997643
+          -0.000000 0.049000 -0.000000
+          1.766666
+          0.049316 -0.044673 -0.022226 0.997536
+          0.000000 0.049000 -0.000000
+          1.800000
+          0.049805 -0.047115 -0.018077 0.997483
+          -0.000000 0.049000 0.000000
+          1.833333
+          0.049561 -0.049314 -0.012218 0.997478
+          0.000000 0.049000 -0.000000
+          1.866666
+          0.048584 -0.051024 -0.007396 0.997488
+          0.000000 0.049000 0.000000
+          1.899999
+          0.047119 -0.053221 -0.005993 0.997452
+          -0.000000 0.049000 -0.000000
+          1.933333
+          0.046875 -0.055663 -0.007031 0.997324
+          0.000000 0.049000 0.000000
+          1.966666
+          0.046631 -0.058104 -0.008069 0.997188
+          -0.000000 0.049000 -0.000000
+          1.999999
+          0.046875 -0.061033 -0.008680 0.996997
+          0.000000 0.049000 0.000000
+          2.033333
+          0.048584 -0.062987 -0.006971 0.996807
+          0.000000 0.049000 -0.000000
+          2.066666
+          0.049561 -0.062012 -0.000670 0.996844
+          -0.000000 0.049000 0.000000
+          2.099999
+          0.050049 -0.059328 0.006091 0.996965
+          -0.000000 0.049000 -0.000000
+          2.133333
+          0.051637 -0.055790 0.011463 0.997041
+          -0.000000 0.049000 -0.000000
+          2.166666
+          0.053223 -0.052250 0.016834 0.997073
+          0.000000 0.049000 -0.000000
+          2.199999
+          0.053467 -0.049565 0.021962 0.997097
+          0.000000 0.049000 -0.000000
+          2.233333
+          0.052978 -0.046636 0.022451 0.997253
+          0.000000 0.049000 0.000000
+          2.266666
+          0.052490 -0.043705 0.018057 0.997501
+          -0.000000 0.049000 0.000000
+          2.299999
+          0.052002 -0.041262 0.011832 0.997724
+          -0.000000 0.049000 0.000000
+          2.333333
+          0.051758 -0.039064 0.007011 0.997871
+          0.000000 0.049000 0.000000
+          2.366666
+          0.051270 -0.037111 0.005119 0.997982
+          0.000000 0.049000 0.000000
+          2.399999
+          0.050781 -0.035646 0.004448 0.998064
+          0.000000 0.049000 0.000000
+          2.433332
+          0.049317 -0.034303 0.003441 0.998188
+          0.000000 0.049000 -0.000000
+          2.466666
+          0.047851 -0.032960 0.002434 0.998308
+          -0.000000 0.048999 0.000000
+          2.499999
+          0.047607 -0.031983 0.000497 0.998354
+          -0.000000 0.049000 -0.000000
+          2.533332
+          0.048584 -0.031982 -0.003181 0.998302
+          0.000000 0.049000 0.000000
+          2.566666
+          0.049805 -0.032957 -0.008796 0.998176
+          -0.000000 0.049000 -0.000000
+          2.599999
+          0.050049 -0.035153 -0.014656 0.998020
+          0.000000 0.049000 0.000000
+          2.633332
+          0.049316 -0.038570 -0.019783 0.997842
+          0.000000 0.049000 -0.000000
+          2.666666
+          0.049072 -0.041987 -0.023202 0.997643
+          0.000000 0.049000 0.000000
+          2.699999
+          0.049072 -0.044429 -0.022226 0.997559
+          -0.000000 0.049000 0.000000
+          2.733332
+          0.049561 -0.047115 -0.018321 0.997491
+          0.000000 0.049000 -0.000000
+          2.766665
+          0.049805 -0.049314 -0.011974 0.997469
+          0.000000 0.049000 -0.000000
+          2.799999
+      "C_Spine_sjnt_2"
+      85
+          0.054125 -0.048828 -0.004743 0.997328
+          -0.000000 0.049000 0.000000
+          0.000000
+          0.053305 -0.050554 -0.000884 0.997297
+          -0.000000 0.049000 -0.000000
+          0.033333
+          0.051989 -0.052780 0.001335 0.997251
+          0.000000 0.049000 0.000000
+          0.066667
+          0.052026 -0.055317 0.001915 0.997111
+          -0.000000 0.049000 0.000000
+          0.100000
+          0.052059 -0.057854 0.002494 0.996964
+          0.000000 0.049000 -0.000000
+          0.133333
+          0.052088 -0.060391 0.003074 0.996810
+          -0.000000 0.049000 -0.000000
+          0.166667
+          0.054473 -0.062355 0.004484 0.996556
+          0.000000 0.049000 0.000000
+          0.200000
+          0.056230 -0.061420 0.010160 0.996475
+          0.000000 0.049000 0.000000
+          0.233333
+          0.056853 -0.058998 0.014774 0.996528
+          0.000000 0.049000 0.000000
+          0.266667
+          0.058931 -0.055818 0.017104 0.996554
+          0.000000 0.049000 0.000000
+          0.300000
+          0.061011 -0.052635 0.019425 0.996559
+          0.000000 0.049000 -0.000000
+          0.333333
+          0.061654 -0.049735 0.023534 0.996580
+          0.000000 0.049000 0.000000
+          0.366667
+          0.060946 -0.046811 0.024178 0.996750
+          -0.000000 0.049000 -0.000000
+          0.400000
+          0.059243 -0.043844 0.019514 0.997089
+          0.000000 0.049000 0.000000
+          0.433333
+          0.057766 -0.041375 0.013884 0.997376
+          -0.000000 0.049000 -0.000000
+          0.466667
+          0.056272 -0.039139 0.009237 0.997605
+          0.000000 0.049000 0.000000
+          0.500000
+          0.055594 -0.037413 0.007542 0.997724
+          0.000000 0.049000 -0.000000
+          0.533333
+          0.054910 -0.035687 0.005849 0.997836
+          0.000000 0.049000 0.000000
+          0.566667
+          0.053731 -0.034328 0.004256 0.997956
+          0.000000 0.049000 0.000000
+          0.600000
+          0.052547 -0.032970 0.002664 0.998071
+          0.000000 0.049000 0.000000
+          0.633333
+          0.052582 -0.031982 0.000834 0.998104
+          0.000000 0.049000 0.000000
+          0.666667
+          0.053984 -0.032202 -0.002376 0.998020
+          0.000000 0.049000 0.000000
+          0.700000
+          0.055389 -0.032920 -0.006620 0.997900
+          0.000000 0.049000 0.000000
+          0.733334
+          0.054958 -0.034862 -0.010018 0.997830
+          0.000000 0.049000 0.000000
+          0.766667
+          0.054518 -0.038155 -0.012228 0.997709
+          0.000000 0.049000 0.000000
+          0.800000
+          0.054082 -0.041447 -0.014438 0.997571
+          -0.000000 0.049000 -0.000000
+          0.833334
+          0.053578 -0.044384 -0.013071 0.997491
+          -0.000000 0.049000 0.000000
+          0.866667
+          0.054104 -0.046609 -0.009549 0.997401
+          -0.000000 0.049000 0.000000
+          0.900000
+          0.053887 -0.048584 -0.005113 0.997351
+          0.000000 0.049000 0.000000
+          0.933334
+          0.053276 -0.050566 -0.000513 0.997299
+          0.000000 0.049000 0.000000
+          0.966667
+          0.052122 -0.053019 0.001311 0.997231
+          0.000000 0.049000 0.000000
+          1.000000
+          0.051939 -0.055482 0.002008 0.997106
+          0.000000 0.049000 0.000000
+          1.033334
+          0.052015 -0.057937 0.002533 0.996961
+          0.000000 0.049000 0.000000
+          1.066667
+          0.052088 -0.060391 0.003059 0.996810
+          -0.000000 0.049000 -0.000000
+          1.100000
+          0.054473 -0.062355 0.004484 0.996556
+          0.000000 0.049000 -0.000000
+          1.133334
+          0.056230 -0.061420 0.010160 0.996475
+          -0.000000 0.049000 0.000000
+          1.166667
+          0.056853 -0.058998 0.014774 0.996528
+          -0.000000 0.049000 -0.000000
+          1.200000
+          0.058930 -0.055818 0.017104 0.996554
+          0.000000 0.049000 0.000000
+          1.233333
+          0.061011 -0.052635 0.019425 0.996559
+          0.000000 0.049000 0.000000
+          1.266667
+          0.061654 -0.049735 0.023534 0.996580
+          0.000000 0.049000 0.000000
+          1.300000
+          0.060946 -0.046811 0.024178 0.996750
+          -0.000000 0.049000 0.000000
+          1.333333
+          0.059243 -0.043844 0.019514 0.997089
+          -0.000000 0.049000 0.000000
+          1.366667
+          0.057766 -0.041375 0.013884 0.997376
+          0.000000 0.049000 0.000000
+          1.400000
+          0.056272 -0.039139 0.009237 0.997605
+          -0.000000 0.049000 -0.000000
+          1.433333
+          0.055594 -0.037413 0.007542 0.997724
+          0.000000 0.049000 0.000000
+          1.466667
+          0.054910 -0.035687 0.005849 0.997836
+          0.000000 0.049000 0.000000
+          1.500000
+          0.053731 -0.034328 0.004256 0.997956
+          0.000000 0.049000 0.000000
+          1.533333
+          0.052548 -0.032970 0.002664 0.998070
+          0.000000 0.049000 -0.000000
+          1.566666
+          0.052583 -0.031982 0.000834 0.998104
+          0.000000 0.049000 0.000000
+          1.600000
+          0.053984 -0.032202 -0.002376 0.998020
+          0.000000 0.049000 0.000000
+          1.633333
+          0.055388 -0.032920 -0.006620 0.997900
+          0.000000 0.049000 -0.000000
+          1.666666
+          0.054959 -0.034862 -0.010018 0.997830
+          0.000000 0.049000 0.000000
+          1.700000
+          0.054518 -0.038155 -0.012228 0.997709
+          0.000000 0.049000 0.000000
+          1.733333
+          0.054082 -0.041447 -0.014438 0.997571
+          -0.000000 0.049000 0.000000
+          1.766666
+          0.053579 -0.044384 -0.013071 0.997491
+          0.000000 0.049001 0.000000
+          1.800000
+          0.054104 -0.046609 -0.009549 0.997401
+          -0.000000 0.049000 0.000000
+          1.833333
+          0.053887 -0.048584 -0.005113 0.997351
+          -0.000000 0.049000 0.000000
+          1.866666
+          0.053275 -0.050566 -0.000513 0.997299
+          -0.000000 0.049000 0.000000
+          1.899999
+          0.052121 -0.053019 0.001311 0.997231
+          0.000000 0.049000 0.000000
+          1.933333
+          0.051938 -0.055482 0.002008 0.997106
+          0.000000 0.049000 0.000000
+          1.966666
+          0.052015 -0.057937 0.002533 0.996961
+          0.000000 0.049000 0.000000
+          1.999999
+          0.052088 -0.060391 0.003059 0.996810
+          -0.000000 0.049000 -0.000000
+          2.033333
+          0.054472 -0.062355 0.004484 0.996556
+          -0.000000 0.049000 0.000000
+          2.066666
+          0.056230 -0.061420 0.010160 0.996475
+          -0.000000 0.049000 -0.000000
+          2.099999
+          0.056853 -0.058998 0.014774 0.996528
+          -0.000000 0.049000 -0.000000
+          2.133333
+          0.058932 -0.055818 0.017104 0.996554
+          -0.000000 0.049000 -0.000000
+          2.166666
+          0.061011 -0.052635 0.019425 0.996559
+          -0.000000 0.049000 0.000000
+          2.199999
+          0.061654 -0.049735 0.023533 0.996580
+          0.000000 0.049000 0.000000
+          2.233333
+          0.060945 -0.046811 0.024178 0.996750
+          0.000000 0.049000 -0.000000
+          2.266666
+          0.059243 -0.043844 0.019515 0.997089
+          -0.000000 0.049000 -0.000000
+          2.299999
+          0.057766 -0.041375 0.013884 0.997376
+          0.000000 0.049000 0.000000
+          2.333333
+          0.056273 -0.039139 0.009237 0.997605
+          -0.000000 0.049000 0.000000
+          2.366666
+          0.055594 -0.037413 0.007542 0.997724
+          0.000000 0.049000 0.000000
+          2.399999
+          0.054911 -0.035687 0.005849 0.997836
+          0.000000 0.049000 0.000000
+          2.433332
+          0.053731 -0.034328 0.004256 0.997956
+          0.000000 0.049000 0.000000
+          2.466666
+          0.052547 -0.032971 0.002664 0.998070
+          0.000000 0.049000 0.000000
+          2.499999
+          0.052582 -0.031981 0.000819 0.998104
+          -0.000000 0.049000 -0.000000
+          2.533332
+          0.053986 -0.032202 -0.002381 0.998020
+          -0.000000 0.049000 0.000000
+          2.566666
+          0.055387 -0.032920 -0.006619 0.997900
+          0.000000 0.049000 0.000000
+          2.599999
+          0.054959 -0.035106 -0.010015 0.997821
+          0.000000 0.049000 -0.000001
+          2.633332
+          0.054517 -0.038277 -0.012226 0.997704
+          0.000000 0.049000 0.000000
+          2.666666
+          0.054080 -0.041447 -0.014438 0.997572
+          -0.000000 0.049000 -0.000001
+          2.699999
+          0.053579 -0.044142 -0.013317 0.997499
+          0.000000 0.049000 0.000000
+          2.733332
+          0.054101 -0.046611 -0.009518 0.997402
+          0.000000 0.049000 -0.000000
+          2.766665
+          0.054124 -0.048828 -0.004743 0.997328
+          0.000000 0.049000 0.000000
+          2.799999
+      "C_Spine_sjnt_4"
+      85
+          0.045865 -0.097519 -0.003639 0.994170
+          0.000000 0.098001 0.000000
+          0.000000
+          0.045526 -0.101279 0.000129 0.993816
+          -0.000000 0.098001 -0.000000
+          0.033333
+          0.045179 -0.105035 0.003897 0.993434
+          0.000000 0.098001 0.000000
+          0.066667
+          0.045116 -0.109419 0.007106 0.992946
+          -0.000000 0.098001 -0.000000
+          0.100000
+          0.045997 -0.114043 0.010673 0.992353
+          -0.000000 0.098001 0.000000
+          0.133333
+          0.046863 -0.118663 0.014244 0.991726
+          -0.000000 0.098001 -0.000000
+          0.166667
+          0.049916 -0.123274 0.016425 0.990980
+          -0.000000 0.098001 -0.000000
+          0.200000
+          0.053007 -0.121530 0.021015 0.990949
+          0.000000 0.098001 0.000000
+          0.233333
+          0.053768 -0.117125 0.023256 0.991388
+          -0.000000 0.098001 0.000000
+          0.266667
+          0.055925 -0.111526 0.022959 0.991921
+          0.000000 0.098001 0.000000
+          0.300000
+          0.058077 -0.105921 0.022655 0.992419
+          0.000000 0.098001 -0.000000
+          0.333333
+          0.058781 -0.100299 0.026230 0.992873
+          0.000000 0.098001 0.000000
+          0.366667
+          0.057111 -0.094223 0.026788 0.993551
+          -0.000000 0.098001 -0.000000
+          0.400000
+          0.053621 -0.088759 0.022782 0.994348
+          -0.000000 0.098001 0.000000
+          0.433333
+          0.050198 -0.083514 0.017076 0.995095
+          0.000000 0.098001 -0.000000
+          0.466667
+          0.046756 -0.078262 0.011380 0.995771
+          -0.000000 0.098001 -0.000000
+          0.500000
+          0.046142 -0.074966 0.007474 0.996090
+          0.000000 0.098001 0.000000
+          0.533333
+          0.046957 -0.071916 0.004281 0.996296
+          0.000000 0.098001 -0.000000
+          0.566667
+          0.047764 -0.069474 0.001153 0.996439
+          -0.000000 0.098001 0.000000
+          0.600000
+          0.048772 -0.067044 -0.000795 0.996557
+          0.000000 0.098001 -0.000000
+          0.633333
+          0.050021 -0.064858 -0.002745 0.996636
+          0.000000 0.098001 -0.000000
+          0.666667
+          0.051143 -0.065735 -0.005382 0.996511
+          0.000000 0.098001 0.000000
+          0.700000
+          0.052265 -0.066611 -0.008019 0.996377
+          0.000000 0.098001 0.000000
+          0.733334
+          0.051035 -0.070051 -0.008947 0.996197
+          0.000000 0.098001 -0.000000
+          0.766667
+          0.049753 -0.076161 -0.008896 0.995814
+          -0.000000 0.098001 0.000000
+          0.800000
+          0.048465 -0.082267 -0.008851 0.995392
+          0.000000 0.098001 0.000000
+          0.833334
+          0.047083 -0.087853 -0.008034 0.994988
+          -0.000000 0.098001 0.000000
+          0.866667
+          0.046959 -0.092566 -0.005846 0.994581
+          -0.000000 0.098001 -0.000000
+          0.900000
+          0.046836 -0.097278 -0.003661 0.994148
+          0.000000 0.098001 0.000000
+          0.933334
+          0.045792 -0.101145 0.000250 0.993817
+          0.000000 0.098001 0.000000
+          0.966667
+          0.044825 -0.105159 0.003423 0.993439
+          -0.000000 0.098001 0.000000
+          1.000000
+          0.045366 -0.109334 0.007159 0.992943
+          0.000000 0.098001 0.000000
+          1.033334
+          0.046123 -0.114001 0.010700 0.992352
+          0.000000 0.098001 0.000000
+          1.066667
+          0.046864 -0.118663 0.014244 0.991726
+          0.000000 0.098001 0.000000
+          1.100000
+          0.049916 -0.123274 0.016425 0.990980
+          0.000000 0.098001 -0.000000
+          1.133334
+          0.053008 -0.121530 0.021015 0.990949
+          0.000000 0.098001 0.000000
+          1.166667
+          0.053768 -0.117125 0.023256 0.991388
+          0.000000 0.098001 -0.000000
+          1.200000
+          0.055926 -0.111526 0.022958 0.991921
+          -0.000000 0.098001 -0.000000
+          1.233333
+          0.058077 -0.105921 0.022655 0.992419
+          0.000000 0.098001 0.000000
+          1.266667
+          0.058781 -0.100299 0.026230 0.992873
+          0.000000 0.098001 0.000000
+          1.300000
+          0.057112 -0.094223 0.026788 0.993551
+          -0.000000 0.098001 0.000000
+          1.333333
+          0.053621 -0.088759 0.022782 0.994348
+          0.000000 0.098001 0.000000
+          1.366667
+          0.050197 -0.083514 0.017077 0.995095
+          0.000000 0.098001 -0.000000
+          1.400000
+          0.046757 -0.078262 0.011380 0.995771
+          0.000000 0.098001 0.000000
+          1.433333
+          0.046141 -0.074966 0.007474 0.996090
+          -0.000000 0.098001 -0.000000
+          1.466667
+          0.046957 -0.071916 0.004281 0.996296
+          -0.000000 0.098001 -0.000000
+          1.500000
+          0.047764 -0.069474 0.001154 0.996439
+          0.000000 0.098001 0.000000
+          1.533333
+          0.048771 -0.067044 -0.000795 0.996557
+          0.000000 0.098001 -0.000000
+          1.566666
+          0.050020 -0.064858 -0.002744 0.996636
+          0.000000 0.098001 0.000000
+          1.600000
+          0.051143 -0.065735 -0.005382 0.996511
+          0.000000 0.098001 -0.000000
+          1.633333
+          0.052265 -0.066611 -0.008019 0.996377
+          -0.000000 0.098001 0.000000
+          1.666666
+          0.051035 -0.070051 -0.008947 0.996197
+          0.000000 0.098001 -0.000000
+          1.700000
+          0.049753 -0.076161 -0.008896 0.995814
+          -0.000000 0.098001 0.000000
+          1.733333
+          0.048465 -0.082267 -0.008851 0.995392
+          -0.000000 0.098001 0.000000
+          1.766666
+          0.047083 -0.087853 -0.008034 0.994988
+          0.000000 0.098001 0.000000
+          1.800000
+          0.046960 -0.092566 -0.005847 0.994581
+          0.000000 0.098001 0.000000
+          1.833333
+          0.046836 -0.097277 -0.003661 0.994148
+          0.000000 0.098001 -0.000000
+          1.866666
+          0.045793 -0.101144 0.000249 0.993817
+          0.000000 0.098001 0.000000
+          1.899999
+          0.044825 -0.105159 0.003423 0.993439
+          0.000000 0.098001 -0.000000
+          1.933333
+          0.045366 -0.109334 0.007159 0.992943
+          0.000000 0.098001 0.000000
+          1.966666
+          0.046123 -0.114000 0.010700 0.992352
+          -0.000000 0.098001 0.000000
+          1.999999
+          0.046863 -0.118663 0.014244 0.991726
+          -0.000000 0.098001 -0.000000
+          2.033333
+          0.049917 -0.123274 0.016424 0.990980
+          0.000000 0.098001 0.000000
+          2.066666
+          0.053007 -0.121530 0.021015 0.990949
+          -0.000000 0.098001 0.000000
+          2.099999
+          0.053769 -0.117125 0.023256 0.991388
+          0.000000 0.098001 -0.000000
+          2.133333
+          0.055925 -0.111526 0.022959 0.991921
+          0.000000 0.098001 0.000000
+          2.166666
+          0.058077 -0.105921 0.022655 0.992419
+          0.000000 0.098001 -0.000000
+          2.199999
+          0.058781 -0.100300 0.026230 0.992873
+          0.000000 0.098001 -0.000000
+          2.233333
+          0.057112 -0.094223 0.026788 0.993551
+          -0.000000 0.098001 0.000000
+          2.266666
+          0.053621 -0.088759 0.022782 0.994348
+          0.000000 0.098001 0.000000
+          2.299999
+          0.050197 -0.083515 0.017077 0.995095
+          0.000000 0.098001 0.000000
+          2.333333
+          0.046756 -0.078262 0.011380 0.995771
+          0.000000 0.098001 -0.000000
+          2.366666
+          0.046141 -0.074966 0.007474 0.996090
+          0.000000 0.098001 0.000000
+          2.399999
+          0.046956 -0.071916 0.004281 0.996296
+          0.000000 0.098001 0.000000
+          2.433332
+          0.047764 -0.069474 0.001154 0.996439
+          0.000000 0.098001 0.000000
+          2.466666
+          0.048773 -0.067044 -0.000826 0.996557
+          0.000000 0.098001 0.000000
+          2.499999
+          0.050022 -0.064858 -0.002813 0.996636
+          -0.000000 0.098001 -0.000000
+          2.533332
+          0.051142 -0.065734 -0.005416 0.996511
+          0.000000 0.098001 0.000000
+          2.566666
+          0.052266 -0.066611 -0.008020 0.996377
+          0.000000 0.098001 -0.000000
+          2.599999
+          0.051036 -0.070051 -0.008948 0.996197
+          0.000000 0.098001 -0.000000
+          2.633332
+          0.049917 -0.076161 -0.008890 0.995806
+          0.000000 0.098001 -0.000000
+          2.666666
+          0.048792 -0.082268 -0.008838 0.995376
+          0.000000 0.098001 0.000000
+          2.699999
+          0.047570 -0.087854 -0.008012 0.994965
+          0.000000 0.098001 0.000000
+          2.733332
+          0.047216 -0.092684 -0.006044 0.994557
+          0.000000 0.098001 0.000000
+          2.766665
+          0.045866 -0.097519 -0.003639 0.994170
+          0.000000 0.098001 0.000000
+          2.799999
+      "L_Clavicle_sjnt_0"
+      85
+          -0.769035 0.591585 0.167503 -0.174803
+          0.007299 0.208857 0.030677
+          0.000000
+          -0.774198 0.587189 0.162131 -0.171872
+          0.007299 0.208857 0.030677
+          0.033333
+          -0.781557 0.578885 0.158711 -0.169918
+          0.007299 0.208857 0.030677
+          0.066667
+          -0.787588 0.571069 0.157733 -0.169429
+          0.007299 0.208857 0.030677
+          0.100000
+          -0.791456 0.565696 0.157732 -0.169428
+          0.007299 0.208857 0.030677
+          0.133333
+          -0.793294 0.563253 0.157243 -0.169428
+          0.007299 0.208857 0.030677
+          0.166667
+          -0.793340 0.563742 0.155778 -0.168939
+          0.007299 0.208857 0.030677
+          0.200000
+          -0.791360 0.566673 0.156267 -0.167963
+          0.007299 0.208857 0.030677
+          0.233333
+          -0.787908 0.571069 0.158221 -0.167475
+          0.007299 0.208857 0.030677
+          0.266667
+          -0.783880 0.576443 0.158222 -0.167964
+          0.007299 0.208857 0.030677
+          0.300000
+          -0.780552 0.581328 0.156270 -0.168453
+          0.007299 0.208857 0.030677
+          0.333333
+          -0.777724 0.585236 0.155783 -0.168454
+          0.007299 0.208857 0.030677
+          0.366667
+          -0.775597 0.587679 0.158224 -0.167478
+          0.007299 0.208857 0.030677
+          0.400000
+          0.774521 -0.588166 -0.161643 0.167478
+          0.007299 0.208857 0.030677
+          0.433333
+          0.774326 -0.587193 -0.164817 0.168700
+          0.007299 0.208857 0.030677
+          0.466667
+          0.774121 -0.586211 -0.167990 0.169919
+          0.007299 0.208857 0.030677
+          0.500000
+          0.771082 -0.588653 -0.171897 0.171385
+          0.007299 0.208857 0.030677
+          0.533333
+          0.766477 -0.593066 -0.175565 0.173099
+          0.007299 0.208857 0.030677
+          0.566667
+          0.761828 -0.597445 -0.179223 0.174804
+          0.007299 0.208857 0.030677
+          0.600000
+          0.758991 -0.599887 -0.181176 0.176758
+          0.007299 0.208857 0.030677
+          0.633333
+          0.757836 -0.599886 -0.182641 0.180176
+          0.007299 0.208857 0.030677
+          0.666667
+          0.758057 -0.598420 -0.183129 0.183594
+          0.007299 0.208857 0.030677
+          0.700000
+          -0.760854 0.595001 0.180687 -0.185546
+          0.007299 0.208857 0.030677
+          0.733334
+          -0.765009 0.591354 0.176052 -0.184574
+          0.007299 0.208857 0.030677
+          0.766667
+          -0.769124 0.587675 0.171408 -0.183592
+          0.007299 0.208857 0.030677
+          0.800000
+          -0.769938 0.587187 0.169943 -0.183103
+          0.007299 0.208857 0.030677
+          0.833334
+          -0.768343 0.589141 0.170920 -0.182615
+          0.007299 0.208857 0.030677
+          0.866667
+          -0.767265 0.591584 0.170432 -0.179686
+          0.007299 0.208857 0.030677
+          0.900000
+          -0.768813 0.591584 0.167503 -0.175779
+          0.007299 0.208857 0.030677
+          0.933334
+          -0.774096 0.587189 0.162619 -0.171872
+          0.007299 0.208857 0.030677
+          0.966667
+          -0.781557 0.578885 0.158711 -0.169918
+          0.007299 0.208857 0.030677
+          1.000000
+          -0.787588 0.571069 0.157733 -0.169429
+          0.007299 0.208857 0.030677
+          1.033334
+          -0.791457 0.565695 0.157732 -0.169428
+          0.007299 0.208857 0.030677
+          1.066667
+          -0.793294 0.563253 0.157243 -0.169428
+          0.007299 0.208857 0.030677
+          1.100000
+          -0.793340 0.563742 0.155778 -0.168939
+          0.007299 0.208857 0.030677
+          1.133334
+          -0.791360 0.566673 0.156267 -0.167963
+          0.007299 0.208857 0.030677
+          1.166667
+          -0.787908 0.571069 0.158221 -0.167475
+          0.007299 0.208857 0.030677
+          1.200000
+          -0.783880 0.576443 0.158222 -0.167964
+          0.007299 0.208857 0.030677
+          1.233333
+          -0.780552 0.581328 0.156270 -0.168453
+          0.007299 0.208857 0.030677
+          1.266667
+          -0.777724 0.585236 0.155783 -0.168454
+          0.007299 0.208857 0.030677
+          1.300000
+          -0.775597 0.587678 0.158224 -0.167477
+          0.007299 0.208857 0.030677
+          1.333333
+          0.774521 -0.588166 -0.161643 0.167478
+          0.007299 0.208857 0.030677
+          1.366667
+          0.774326 -0.587192 -0.164817 0.168700
+          0.007299 0.208857 0.030677
+          1.400000
+          0.774121 -0.586211 -0.167990 0.169919
+          0.007299 0.208857 0.030677
+          1.433333
+          0.771082 -0.588653 -0.171897 0.171384
+          0.007299 0.208858 0.030677
+          1.466667
+          0.766477 -0.593066 -0.175565 0.173099
+          0.007299 0.208857 0.030677
+          1.500000
+          0.761828 -0.597445 -0.179223 0.174804
+          0.007299 0.208857 0.030677
+          1.533333
+          0.758991 -0.599887 -0.181176 0.176758
+          0.007299 0.208857 0.030677
+          1.566666
+          0.757836 -0.599886 -0.182641 0.180176
+          0.007299 0.208857 0.030677
+          1.600000
+          0.758057 -0.598421 -0.183129 0.183594
+          0.007299 0.208857 0.030677
+          1.633333
+          -0.760854 0.595001 0.180687 -0.185546
+          0.007299 0.208857 0.030677
+          1.666666
+          -0.765009 0.591354 0.176052 -0.184574
+          0.007299 0.208857 0.030677
+          1.700000
+          -0.769124 0.587675 0.171408 -0.183592
+          0.007299 0.208857 0.030677
+          1.733333
+          -0.769938 0.587187 0.169943 -0.183103
+          0.007299 0.208857 0.030677
+          1.766666
+          -0.768343 0.589141 0.170920 -0.182615
+          0.007299 0.208857 0.030677
+          1.800000
+          -0.767265 0.591584 0.170432 -0.179686
+          0.007299 0.208857 0.030677
+          1.833333
+          -0.768812 0.591585 0.167503 -0.175779
+          0.007299 0.208857 0.030677
+          1.866666
+          -0.774096 0.587189 0.162619 -0.171872
+          0.007299 0.208857 0.030677
+          1.899999
+          -0.781557 0.578885 0.158711 -0.169918
+          0.007299 0.208857 0.030677
+          1.933333
+          -0.787588 0.571069 0.157733 -0.169429
+          0.007299 0.208857 0.030677
+          1.966666
+          -0.791456 0.565696 0.157732 -0.169428
+          0.007299 0.208857 0.030677
+          1.999999
+          -0.793294 0.563253 0.157243 -0.169428
+          0.007299 0.208857 0.030677
+          2.033333
+          -0.793340 0.563742 0.155778 -0.168939
+          0.007299 0.208857 0.030677
+          2.066666
+          -0.791360 0.566673 0.156267 -0.167963
+          0.007299 0.208857 0.030677
+          2.099999
+          -0.787908 0.571069 0.158221 -0.167475
+          0.007299 0.208857 0.030677
+          2.133333
+          -0.783880 0.576443 0.158222 -0.167964
+          0.007299 0.208857 0.030677
+          2.166666
+          -0.780552 0.581328 0.156270 -0.168453
+          0.007299 0.208858 0.030677
+          2.199999
+          -0.777724 0.585236 0.155783 -0.168454
+          0.007299 0.208857 0.030677
+          2.233333
+          -0.775597 0.587678 0.158224 -0.167478
+          0.007299 0.208857 0.030677
+          2.266666
+          0.774522 -0.588166 -0.161643 0.167478
+          0.007299 0.208857 0.030677
+          2.299999
+          0.774326 -0.587193 -0.164817 0.168699
+          0.007299 0.208857 0.030677
+          2.333333
+          0.774121 -0.586211 -0.167990 0.169919
+          0.007299 0.208857 0.030677
+          2.366666
+          0.771082 -0.588653 -0.171897 0.171385
+          0.007299 0.208857 0.030677
+          2.399999
+          0.766477 -0.593066 -0.175565 0.173099
+          0.007299 0.208857 0.030677
+          2.433332
+          0.761828 -0.597445 -0.179223 0.174804
+          0.007299 0.208857 0.030677
+          2.466666
+          0.758991 -0.599887 -0.181176 0.176758
+          0.007299 0.208857 0.030677
+          2.499999
+          0.757952 -0.599887 -0.182641 0.179687
+          0.007299 0.208857 0.030677
+          2.533332
+          0.758057 -0.598420 -0.183129 0.183593
+          0.007299 0.208857 0.030677
+          2.566666
+          -0.760854 0.595001 0.180687 -0.185546
+          0.007299 0.208857 0.030677
+          2.599999
+          -0.765151 0.591094 0.175804 -0.185057
+          0.007299 0.208857 0.030677
+          2.633332
+          -0.768750 0.588164 0.171408 -0.183592
+          0.007299 0.208857 0.030676
+          2.666666
+          -0.769565 0.587675 0.169943 -0.183103
+          0.007299 0.208857 0.030677
+          2.699999
+          -0.768452 0.589141 0.170432 -0.182615
+          0.007299 0.208858 0.030677
+          2.733332
+          -0.767527 0.591095 0.170432 -0.180174
+          0.007299 0.208857 0.030677
+          2.766665
+          -0.769035 0.591584 0.167503 -0.174803
+          0.007299 0.208857 0.030677
+          2.799999
+      "L_Arm_sjnt_0"
+      85
+          -0.131821 0.222491 -0.641938 -0.721829
+          0.070455 -0.150704 0.005819
+          0.000000
+          -0.143059 0.208818 -0.639497 -0.725929
+          0.070455 -0.150703 0.005819
+          0.033333
+          -0.158692 0.196123 -0.638035 -0.727505
+          0.070455 -0.150704 0.005819
+          0.066667
+          -0.179701 0.185870 -0.631201 -0.731263
+          0.070455 -0.150704 0.005819
+          0.100000
+          -0.203155 0.176593 -0.619971 -0.737007
+          0.070455 -0.150704 0.005819
+          0.133333
+          -0.219280 0.166827 -0.611182 -0.741985
+          0.070455 -0.150703 0.005819
+          0.166667
+          -0.223677 0.157550 -0.611182 -0.742700
+          0.070455 -0.150703 0.005820
+          0.200000
+          -0.219766 0.152179 -0.618508 -0.738913
+          0.070455 -0.150703 0.005820
+          0.233333
+          -0.212434 0.152667 -0.628765 -0.732270
+          0.070455 -0.150703 0.005819
+          0.266667
+          -0.204612 0.159503 -0.640489 -0.722819
+          0.070455 -0.150703 0.005819
+          0.300000
+          -0.199234 0.169269 -0.649770 -0.713760
+          0.070456 -0.150703 0.005820
+          0.333333
+          -0.198742 0.178547 -0.655145 -0.706688
+          0.070455 -0.150704 0.005819
+          0.366667
+          -0.198251 0.184895 -0.659542 -0.701081
+          0.070455 -0.150704 0.005819
+          0.400000
+          0.188478 -0.188802 0.665891 0.696721
+          0.070455 -0.150704 0.005819
+          0.433333
+          0.166981 -0.194172 0.673701 0.693211
+          0.070455 -0.150704 0.005819
+          0.466667
+          0.138155 -0.202961 0.680044 0.690840
+          0.070455 -0.150703 0.005820
+          0.500000
+          0.108354 -0.213702 0.682967 0.690034
+          0.070455 -0.150704 0.005819
+          0.533333
+          0.083441 -0.224444 0.681495 0.691540
+          0.070455 -0.150704 0.005819
+          0.566667
+          0.064390 -0.232256 0.677581 0.694835
+          0.070455 -0.150703 0.005820
+          0.600000
+          0.052178 -0.236651 0.675135 0.696755
+          0.070455 -0.150703 0.005819
+          0.633333
+          0.046316 -0.236162 0.674645 0.697809
+          0.070455 -0.150703 0.005820
+          0.666667
+          0.044361 -0.230303 0.676598 0.698003
+          0.070455 -0.150704 0.005819
+          0.700000
+          -0.042409 0.219092 -0.683253 -0.695245
+          0.070455 -0.150703 0.005819
+          0.733334
+          -0.040449 0.207841 -0.689785 -0.692361
+          0.070455 -0.150703 0.005819
+          0.766667
+          -0.048753 0.206377 -0.690276 -0.691774
+          0.070455 -0.150704 0.005819
+          0.800000
+          -0.070252 0.216143 -0.681001 -0.696122
+          0.070455 -0.150704 0.005819
+          0.833334
+          -0.097126 0.227374 -0.664889 -0.704834
+          0.070455 -0.150704 0.005819
+          0.866667
+          -0.118628 0.230303 -0.649751 -0.714641
+          0.070456 -0.150703 0.005820
+          0.900000
+          -0.132310 0.222490 -0.641449 -0.722174
+          0.070455 -0.150703 0.005820
+          0.933334
+          -0.143059 0.208818 -0.639497 -0.725929
+          0.070455 -0.150703 0.005819
+          0.966667
+          -0.157715 0.196123 -0.638523 -0.727290
+          0.070455 -0.150704 0.005819
+          1.000000
+          -0.179701 0.185870 -0.631689 -0.730841
+          0.070455 -0.150703 0.005820
+          1.033334
+          -0.203155 0.176593 -0.619971 -0.737007
+          0.070455 -0.150704 0.005820
+          1.066667
+          -0.219280 0.166827 -0.611182 -0.741985
+          0.070455 -0.150704 0.005819
+          1.100000
+          -0.223188 0.157550 -0.611182 -0.742847
+          0.070455 -0.150704 0.005819
+          1.133334
+          -0.219766 0.152179 -0.618508 -0.738913
+          0.070455 -0.150703 0.005820
+          1.166667
+          -0.212433 0.152667 -0.628766 -0.732270
+          0.070455 -0.150704 0.005819
+          1.200000
+          -0.204612 0.159503 -0.640489 -0.722819
+          0.070455 -0.150703 0.005819
+          1.233333
+          -0.199234 0.169269 -0.649770 -0.713760
+          0.070455 -0.150703 0.005819
+          1.266667
+          -0.198742 0.178547 -0.655145 -0.706688
+          0.070455 -0.150703 0.005820
+          1.300000
+          -0.198252 0.184895 -0.659542 -0.701081
+          0.070455 -0.150703 0.005819
+          1.333333
+          0.188478 -0.188802 0.665891 0.696721
+          0.070455 -0.150704 0.005819
+          1.366667
+          0.166981 -0.194172 0.673701 0.693211
+          0.070455 -0.150703 0.005819
+          1.400000
+          0.138155 -0.202961 0.680044 0.690840
+          0.070455 -0.150704 0.005820
+          1.433333
+          0.108355 -0.213702 0.682967 0.690034
+          0.070456 -0.150704 0.005820
+          1.466667
+          0.083441 -0.224444 0.681494 0.691540
+          0.070456 -0.150704 0.005819
+          1.500000
+          0.064390 -0.232256 0.677581 0.694835
+          0.070456 -0.150703 0.005820
+          1.533333
+          0.052178 -0.236651 0.675135 0.696755
+          0.070455 -0.150704 0.005820
+          1.566666
+          0.046316 -0.236162 0.674645 0.697809
+          0.070456 -0.150704 0.005820
+          1.600000
+          0.044361 -0.230303 0.676597 0.698003
+          0.070456 -0.150703 0.005819
+          1.633333
+          -0.042409 0.219092 -0.683253 -0.695245
+          0.070455 -0.150703 0.005819
+          1.666666
+          -0.040449 0.207842 -0.689785 -0.692361
+          0.070455 -0.150703 0.005819
+          1.700000
+          -0.048753 0.206377 -0.690276 -0.691774
+          0.070456 -0.150704 0.005820
+          1.733333
+          -0.070251 0.216143 -0.681001 -0.696121
+          0.070455 -0.150704 0.005819
+          1.766666
+          -0.097126 0.227373 -0.664889 -0.704834
+          0.070455 -0.150703 0.005819
+          1.800000
+          -0.118627 0.230303 -0.649751 -0.714641
+          0.070456 -0.150703 0.005819
+          1.833333
+          -0.132310 0.222491 -0.641449 -0.722174
+          0.070456 -0.150703 0.005820
+          1.866666
+          -0.143058 0.208819 -0.639497 -0.725929
+          0.070455 -0.150703 0.005819
+          1.899999
+          -0.157715 0.196123 -0.638523 -0.727290
+          0.070455 -0.150703 0.005820
+          1.933333
+          -0.179701 0.185870 -0.631690 -0.730841
+          0.070455 -0.150703 0.005819
+          1.966666
+          -0.203154 0.176593 -0.619971 -0.737007
+          0.070456 -0.150704 0.005819
+          1.999999
+          -0.219279 0.166827 -0.611182 -0.741986
+          0.070456 -0.150704 0.005819
+          2.033333
+          -0.223188 0.157550 -0.611182 -0.742847
+          0.070455 -0.150703 0.005820
+          2.066666
+          -0.219766 0.152179 -0.618508 -0.738913
+          0.070456 -0.150704 0.005819
+          2.099999
+          -0.212434 0.152667 -0.628765 -0.732270
+          0.070456 -0.150703 0.005820
+          2.133333
+          -0.204612 0.159503 -0.640488 -0.722819
+          0.070455 -0.150704 0.005819
+          2.166666
+          -0.199234 0.169269 -0.649770 -0.713760
+          0.070455 -0.150704 0.005819
+          2.199999
+          -0.198742 0.178547 -0.655145 -0.706688
+          0.070456 -0.150704 0.005819
+          2.233333
+          -0.198252 0.184895 -0.659542 -0.701081
+          0.070456 -0.150704 0.005820
+          2.266666
+          0.188479 -0.188801 0.665891 0.696721
+          0.070455 -0.150703 0.005819
+          2.299999
+          0.166981 -0.194172 0.673701 0.693211
+          0.070456 -0.150703 0.005820
+          2.333333
+          0.138156 -0.202960 0.680044 0.690841
+          0.070456 -0.150703 0.005819
+          2.366666
+          0.108355 -0.213702 0.682967 0.690034
+          0.070456 -0.150703 0.005820
+          2.399999
+          0.082953 -0.224444 0.681495 0.691599
+          0.070455 -0.150704 0.005820
+          2.433332
+          0.064390 -0.232256 0.677581 0.694835
+          0.070455 -0.150704 0.005819
+          2.466666
+          0.052178 -0.236651 0.675135 0.696755
+          0.070455 -0.150703 0.005820
+          2.499999
+          0.046316 -0.236162 0.674156 0.698281
+          0.070455 -0.150704 0.005819
+          2.533332
+          0.044362 -0.230303 0.676597 0.698003
+          0.070455 -0.150704 0.005819
+          2.566666
+          -0.041428 0.218584 -0.682946 -0.695765
+          0.070456 -0.150703 0.005819
+          2.599999
+          -0.039960 0.207842 -0.689784 -0.692389
+          0.070456 -0.150703 0.005819
+          2.633332
+          -0.048753 0.206377 -0.690276 -0.691774
+          0.070455 -0.150703 0.005819
+          2.666666
+          -0.070251 0.216142 -0.680513 -0.696599
+          0.070456 -0.150703 0.005820
+          2.699999
+          -0.096637 0.226885 -0.664889 -0.705059
+          0.070455 -0.150703 0.005819
+          2.733332
+          -0.119115 0.230303 -0.650240 -0.714115
+          0.070455 -0.150703 0.005819
+          2.766665
+          -0.131820 0.222491 -0.641938 -0.721829
+          0.070456 -0.150704 0.005819
+          2.799999
+      "L_Arm_sjnt_1"
+      85
+          0.295580 -0.012448 -0.055640 0.953615
+          -0.000001 -0.260631 -0.000000
+          0.000000
+          0.327818 0.016122 -0.054900 0.943007
+          -0.000001 -0.260631 -0.000000
+          0.033333
+          0.356640 0.020033 -0.056108 0.932340
+          -0.000001 -0.260631 -0.000000
+          0.066667
+          0.383023 0.013323 -0.059024 0.921755
+          -0.000001 -0.260631 -0.000000
+          0.100000
+          0.406475 0.003987 -0.062428 0.911518
+          -0.000001 -0.260631 -0.000001
+          0.133333
+          0.426995 -0.002236 -0.065346 0.901887
+          -0.000001 -0.260631 -0.000000
+          0.166667
+          0.442629 -0.005957 -0.067779 0.894120
+          -0.000001 -0.260631 -0.000000
+          0.200000
+          0.449958 -0.008275 -0.069240 0.890323
+          -0.000001 -0.260631 -0.000000
+          0.233333
+          0.446049 -0.007544 -0.068754 0.892332
+          -0.000001 -0.260631 -0.000001
+          0.266667
+          0.429926 -0.003273 -0.065833 0.900455
+          -0.000001 -0.260631 -0.000000
+          0.300000
+          0.403056 0.000050 -0.062429 0.913043
+          -0.000001 -0.260631 -0.000000
+          0.333333
+          0.369836 -0.002946 -0.060002 0.927153
+          -0.000001 -0.260631 -0.000000
+          0.366667
+          0.337105 -0.010855 -0.058551 0.939582
+          -0.000001 -0.260631 -0.000001
+          0.400000
+          0.313657 -0.016596 -0.057584 0.947643
+          -0.000001 -0.260631 -0.000000
+          0.433333
+          0.305352 -0.016353 -0.056611 0.950415
+          -0.000001 -0.260631 -0.000000
+          0.466667
+          0.312679 -0.012933 -0.056365 0.948097
+          -0.000001 -0.260631 -0.000000
+          0.500000
+          0.328311 -0.010612 -0.057090 0.942783
+          -0.000001 -0.260631 -0.000000
+          0.533333
+          0.340525 -0.009267 -0.058550 0.938365
+          -0.000001 -0.260631 -0.000000
+          0.566667
+          0.341502 -0.007436 -0.059282 0.937980
+          -0.000001 -0.260631 -0.000000
+          0.600000
+          0.326845 -0.005058 -0.058069 0.943279
+          -0.000001 -0.260631 -0.000000
+          0.633333
+          0.296067 -0.004147 -0.055398 0.953550
+          -0.000001 -0.260631 -0.000000
+          0.666667
+          0.255030 -0.007265 -0.051754 0.965520
+          -0.000001 -0.260631 -0.000000
+          0.700000
+          0.214483 -0.016853 -0.048352 0.975385
+          -0.000001 -0.260631 -0.000000
+          0.733334
+          0.187616 -0.032726 -0.045431 0.980645
+          -0.000001 -0.260631 -0.000000
+          0.766667
+          0.182244 -0.049328 -0.044452 0.981009
+          -0.000001 -0.260631 -0.000000
+          0.800000
+          0.198368 -0.057138 -0.046396 0.977360
+          -0.000001 -0.260631 -0.000000
+          0.833334
+          0.228168 -0.050787 -0.050291 0.970995
+          -0.000001 -0.260631 -0.000001
+          0.866667
+          0.262363 -0.031007 -0.053698 0.962975
+          -0.000001 -0.260631 -0.000000
+          0.900000
+          0.296068 -0.005245 -0.054910 0.953573
+          -0.000001 -0.260631 -0.000000
+          0.933334
+          0.327819 0.014413 -0.055144 0.943020
+          -0.000001 -0.260631 -0.000000
+          0.966667
+          0.356641 0.020033 -0.056108 0.932340
+          -0.000001 -0.260631 -0.000000
+          1.000000
+          0.383023 0.013689 -0.059024 0.921749
+          -0.000001 -0.260631 -0.000000
+          1.033334
+          0.406475 0.004109 -0.062428 0.911517
+          -0.000001 -0.260631 -0.000000
+          1.066667
+          0.426995 -0.002358 -0.065346 0.901887
+          -0.000001 -0.260631 -0.000000
+          1.100000
+          0.442629 -0.006079 -0.067779 0.894119
+          -0.000001 -0.260631 -0.000000
+          1.133334
+          0.449958 -0.008276 -0.069240 0.890323
+          -0.000001 -0.260631 -0.000000
+          1.166667
+          0.446049 -0.007544 -0.068754 0.892332
+          -0.000001 -0.260631 -0.000000
+          1.200000
+          0.429926 -0.003242 -0.065833 0.900455
+          -0.000001 -0.260631 -0.000000
+          1.233333
+          0.403056 0.000050 -0.062429 0.913043
+          -0.000001 -0.260631 -0.000000
+          1.266667
+          0.369836 -0.002946 -0.060002 0.927153
+          -0.000001 -0.260631 -0.000001
+          1.300000
+          0.337105 -0.010855 -0.058551 0.939582
+          -0.000001 -0.260631 -0.000000
+          1.333333
+          0.313657 -0.016596 -0.057584 0.947643
+          -0.000001 -0.260631 -0.000000
+          1.366667
+          0.305352 -0.016353 -0.056611 0.950415
+          -0.000001 -0.260631 -0.000000
+          1.400000
+          0.312679 -0.012933 -0.056365 0.948097
+          -0.000001 -0.260631 -0.000000
+          1.433333
+          0.328311 -0.010612 -0.057090 0.942783
+          -0.000001 -0.260631 -0.000000
+          1.466667
+          0.340525 -0.009267 -0.058550 0.938365
+          -0.000001 -0.260631 -0.000001
+          1.500000
+          0.341502 -0.007436 -0.059282 0.937980
+          -0.000001 -0.260631 -0.000000
+          1.533333
+          0.326846 -0.005058 -0.058069 0.943278
+          -0.000001 -0.260631 -0.000000
+          1.566666
+          0.296068 -0.004147 -0.055398 0.953550
+          -0.000001 -0.260631 -0.000000
+          1.600000
+          0.255031 -0.007265 -0.051754 0.965520
+          -0.000001 -0.260631 0.000000
+          1.633333
+          0.214484 -0.016853 -0.048352 0.975384
+          -0.000001 -0.260631 -0.000001
+          1.666666
+          0.187616 -0.032726 -0.045431 0.980645
+          -0.000001 -0.260631 0.000000
+          1.700000
+          0.182244 -0.049328 -0.044452 0.981009
+          -0.000001 -0.260631 -0.000001
+          1.733333
+          0.198367 -0.057138 -0.046396 0.977360
+          -0.000001 -0.260631 -0.000000
+          1.766666
+          0.228168 -0.050787 -0.050291 0.970995
+          -0.000001 -0.260631 -0.000000
+          1.800000
+          0.262362 -0.031007 -0.053698 0.962975
+          -0.000001 -0.260631 -0.000000
+          1.833333
+          0.296067 -0.005246 -0.054910 0.953573
+          -0.000001 -0.260631 -0.000000
+          1.866666
+          0.327818 0.014413 -0.055144 0.943020
+          -0.000001 -0.260631 -0.000000
+          1.899999
+          0.356640 0.020033 -0.056108 0.932340
+          -0.000001 -0.260631 -0.000000
+          1.933333
+          0.383022 0.013689 -0.059024 0.921750
+          -0.000001 -0.260631 -0.000000
+          1.966666
+          0.406474 0.004109 -0.062428 0.911518
+          -0.000001 -0.260631 -0.000000
+          1.999999
+          0.426994 -0.002358 -0.065346 0.901887
+          -0.000001 -0.260631 -0.000001
+          2.033333
+          0.442629 -0.006079 -0.067779 0.894119
+          -0.000001 -0.260631 -0.000000
+          2.066666
+          0.449958 -0.008275 -0.069240 0.890323
+          -0.000001 -0.260631 -0.000000
+          2.099999
+          0.446049 -0.007544 -0.068754 0.892332
+          -0.000001 -0.260631 -0.000001
+          2.133333
+          0.429927 -0.003243 -0.065833 0.900455
+          -0.000002 -0.260631 0.000000
+          2.166666
+          0.403056 0.000050 -0.062429 0.913043
+          -0.000001 -0.260631 0.000000
+          2.199999
+          0.369836 -0.002946 -0.060002 0.927153
+          -0.000001 -0.260631 -0.000000
+          2.233333
+          0.337106 -0.010855 -0.058551 0.939581
+          -0.000001 -0.260631 -0.000001
+          2.266666
+          0.313658 -0.016595 -0.057584 0.947643
+          -0.000001 -0.260631 0.000000
+          2.299999
+          0.305352 -0.016353 -0.056611 0.950415
+          -0.000001 -0.260631 -0.000001
+          2.333333
+          0.312679 -0.012933 -0.056365 0.948097
+          -0.000001 -0.260631 -0.000001
+          2.366666
+          0.328311 -0.010490 -0.057090 0.942784
+          -0.000001 -0.260631 -0.000000
+          2.399999
+          0.340524 -0.009267 -0.058550 0.938365
+          -0.000001 -0.260631 -0.000001
+          2.433332
+          0.341502 -0.007497 -0.059282 0.937980
+          -0.000001 -0.260631 -0.000001
+          2.466666
+          0.326846 -0.005119 -0.058313 0.943263
+          -0.000001 -0.260631 -0.000000
+          2.499999
+          0.296069 -0.004086 -0.055398 0.953550
+          -0.000001 -0.260631 -0.000000
+          2.533332
+          0.255032 -0.007082 -0.051754 0.965521
+          -0.000001 -0.260631 -0.000000
+          2.566666
+          0.214485 -0.017097 -0.048352 0.975380
+          -0.000001 -0.260631 -0.000000
+          2.599999
+          0.187129 -0.033214 -0.045431 0.980722
+          -0.000001 -0.260631 -0.000000
+          2.633332
+          0.182245 -0.049816 -0.044452 0.980984
+          -0.000001 -0.260631 -0.000001
+          2.666666
+          0.198367 -0.056162 -0.046396 0.977417
+          -0.000001 -0.260632 -0.000000
+          2.699999
+          0.228167 -0.049323 -0.050291 0.971070
+          -0.000001 -0.260631 0.000000
+          2.733332
+          0.261873 -0.032717 -0.053698 0.963052
+          -0.000001 -0.260631 0.000000
+          2.766665
+          0.295579 -0.012449 -0.055640 0.953615
+          -0.000001 -0.260631 -0.000000
+          2.799999
+      "L_Wrist_sjnt_0"
+      85
+          -0.005086 0.125501 -0.185093 0.974661
+          0.000007 -0.242273 0.000002
+          0.000000
+          0.010416 0.128437 -0.184110 0.974422
+          0.000007 -0.242273 0.000002
+          0.033333
+          0.000879 0.126481 -0.182649 0.975008
+          0.000007 -0.242273 0.000002
+          0.066667
+          -0.003924 0.125012 -0.188510 0.974074
+          0.000007 -0.242273 0.000002
+          0.100000
+          0.002639 0.125990 -0.194367 0.972801
+          0.000007 -0.242273 0.000002
+          0.133333
+          0.025189 0.130881 -0.191429 0.972415
+          0.000007 -0.242273 0.000002
+          0.166667
+          0.046428 0.134795 -0.192398 0.970906
+          0.000007 -0.242273 0.000002
+          0.200000
+          0.036670 0.132834 -0.208027 0.968367
+          0.000007 -0.242273 0.000002
+          0.233333
+          0.034721 0.132342 -0.219746 0.965915
+          0.000007 -0.242273 0.000002
+          0.266667
+          0.020807 0.129667 -0.215381 0.967659
+          0.000007 -0.242273 0.000002
+          0.300000
+          0.006887 0.126963 -0.210967 0.969188
+          0.000007 -0.242273 0.000002
+          0.333333
+          0.007860 0.127454 -0.202178 0.970988
+          0.000007 -0.242273 0.000002
+          0.366667
+          -0.003590 0.125990 -0.186557 0.974326
+          0.000007 -0.242273 0.000002
+          0.400000
+          -0.009972 0.125014 -0.176305 0.976314
+          0.000007 -0.242273 0.000002
+          0.433333
+          -0.011803 0.124525 -0.175818 0.976444
+          0.000007 -0.242274 0.000002
+          0.466667
+          -0.022668 0.122569 -0.171427 0.977280
+          0.000007 -0.242273 0.000002
+          0.500000
+          -0.019250 0.123546 -0.173379 0.976886
+          0.000006 -0.242273 0.000002
+          0.533333
+          -0.010978 0.124775 -0.178512 0.975932
+          0.000007 -0.242273 0.000002
+          0.566667
+          -0.002706 0.125991 -0.183627 0.974885
+          0.000007 -0.242273 0.000002
+          0.600000
+          -0.002552 0.125990 -0.186068 0.974422
+          0.000007 -0.242273 0.000002
+          0.633333
+          -0.002523 0.125991 -0.182650 0.975069
+          0.000007 -0.242273 0.000002
+          0.666667
+          -0.018757 0.123055 -0.185097 0.974805
+          0.000007 -0.242273 0.000002
+          0.700000
+          -0.048298 0.117674 -0.182666 0.974912
+          0.000007 -0.242273 0.000002
+          0.733334
+          -0.066366 0.114740 -0.176814 0.975278
+          0.000007 -0.242273 0.000002
+          0.766667
+          -0.067344 0.115229 -0.172908 0.975854
+          0.000007 -0.242273 0.000002
+          0.800000
+          -0.069300 0.115719 -0.164120 0.977175
+          0.000006 -0.242273 0.000002
+          0.833334
+          -0.066372 0.116698 -0.161189 0.977750
+          0.000007 -0.242273 0.000002
+          0.866667
+          -0.042932 0.119633 -0.169970 0.977218
+          0.000007 -0.242273 0.000002
+          0.900000
+          -0.005085 0.125501 -0.185093 0.974661
+          0.000007 -0.242273 0.000002
+          0.933334
+          0.010416 0.128437 -0.184111 0.974422
+          0.000006 -0.242274 0.000002
+          0.966667
+          0.000879 0.126481 -0.182649 0.975008
+          0.000007 -0.242273 0.000002
+          1.000000
+          -0.003924 0.125012 -0.188510 0.974074
+          0.000007 -0.242274 0.000002
+          1.033334
+          0.002639 0.125990 -0.194367 0.972801
+          0.000006 -0.242273 0.000002
+          1.066667
+          0.025189 0.130882 -0.191429 0.972415
+          0.000006 -0.242273 0.000002
+          1.100000
+          0.046428 0.134795 -0.192398 0.970906
+          0.000006 -0.242273 0.000002
+          1.133334
+          0.036670 0.132834 -0.208027 0.968367
+          0.000007 -0.242273 0.000002
+          1.166667
+          0.034721 0.132342 -0.219746 0.965915
+          0.000007 -0.242273 0.000002
+          1.200000
+          0.020807 0.129667 -0.215381 0.967659
+          0.000007 -0.242273 0.000002
+          1.233333
+          0.006887 0.126963 -0.210967 0.969188
+          0.000007 -0.242273 0.000002
+          1.266667
+          0.007860 0.127454 -0.202178 0.970988
+          0.000007 -0.242273 0.000002
+          1.300000
+          -0.003590 0.125990 -0.186557 0.974326
+          0.000006 -0.242274 0.000002
+          1.333333
+          -0.009972 0.125014 -0.176305 0.976314
+          0.000006 -0.242274 0.000002
+          1.366667
+          -0.011803 0.124525 -0.175818 0.976444
+          0.000006 -0.242273 0.000002
+          1.400000
+          -0.022668 0.122569 -0.171427 0.977280
+          0.000006 -0.242273 0.000002
+          1.433333
+          -0.019250 0.123546 -0.173379 0.976886
+          0.000007 -0.242273 0.000002
+          1.466667
+          -0.010979 0.124775 -0.178512 0.975932
+          0.000007 -0.242274 0.000002
+          1.500000
+          -0.002706 0.125991 -0.183627 0.974885
+          0.000006 -0.242273 0.000002
+          1.533333
+          -0.002552 0.125990 -0.186068 0.974422
+          0.000007 -0.242273 0.000002
+          1.566666
+          -0.002523 0.125991 -0.182650 0.975069
+          0.000006 -0.242273 0.000002
+          1.600000
+          -0.018757 0.123055 -0.185097 0.974805
+          0.000007 -0.242273 0.000002
+          1.633333
+          -0.048297 0.117674 -0.182666 0.974912
+          0.000006 -0.242273 0.000003
+          1.666666
+          -0.066365 0.114740 -0.176814 0.975278
+          0.000007 -0.242274 0.000002
+          1.700000
+          -0.067344 0.115229 -0.172908 0.975854
+          0.000006 -0.242273 0.000002
+          1.733333
+          -0.069300 0.115719 -0.164120 0.977175
+          0.000006 -0.242274 0.000002
+          1.766666
+          -0.066372 0.116698 -0.161189 0.977750
+          0.000006 -0.242273 0.000002
+          1.800000
+          -0.042932 0.119633 -0.169969 0.977218
+          0.000007 -0.242273 0.000002
+          1.833333
+          -0.005086 0.125501 -0.185092 0.974661
+          0.000007 -0.242273 0.000002
+          1.866666
+          0.010416 0.128437 -0.184110 0.974422
+          0.000007 -0.242274 0.000002
+          1.899999
+          0.000879 0.126481 -0.182649 0.975008
+          0.000006 -0.242273 0.000002
+          1.933333
+          -0.003924 0.125012 -0.188510 0.974074
+          0.000006 -0.242273 0.000002
+          1.966666
+          0.002639 0.125990 -0.194367 0.972801
+          0.000007 -0.242273 0.000002
+          1.999999
+          0.025188 0.130881 -0.191429 0.972415
+          0.000006 -0.242274 0.000003
+          2.033333
+          0.046428 0.134795 -0.192398 0.970906
+          0.000006 -0.242274 0.000002
+          2.066666
+          0.036670 0.132834 -0.208026 0.968368
+          0.000007 -0.242274 0.000002
+          2.099999
+          0.034721 0.132342 -0.219746 0.965915
+          0.000006 -0.242273 0.000002
+          2.133333
+          0.020807 0.129667 -0.215381 0.967659
+          0.000007 -0.242273 0.000002
+          2.166666
+          0.006888 0.126964 -0.210967 0.969188
+          0.000006 -0.242273 0.000002
+          2.199999
+          0.007860 0.127455 -0.202178 0.970988
+          0.000007 -0.242273 0.000002
+          2.233333
+          -0.003589 0.125990 -0.186557 0.974326
+          0.000007 -0.242273 0.000003
+          2.266666
+          -0.009972 0.125014 -0.176306 0.976314
+          0.000006 -0.242273 0.000003
+          2.299999
+          -0.011803 0.124525 -0.175818 0.976444
+          0.000006 -0.242273 0.000002
+          2.333333
+          -0.022668 0.122569 -0.171427 0.977280
+          0.000007 -0.242273 0.000002
+          2.366666
+          -0.019250 0.123546 -0.173379 0.976886
+          0.000007 -0.242273 0.000002
+          2.399999
+          -0.010979 0.124775 -0.178511 0.975933
+          0.000007 -0.242273 0.000002
+          2.433332
+          -0.002706 0.125991 -0.183627 0.974885
+          0.000007 -0.242273 0.000003
+          2.466666
+          -0.002552 0.125990 -0.186068 0.974422
+          0.000007 -0.242273 0.000002
+          2.499999
+          -0.002523 0.125991 -0.182650 0.975069
+          0.000007 -0.242274 0.000003
+          2.533332
+          -0.018756 0.123055 -0.185097 0.974805
+          0.000007 -0.242273 0.000002
+          2.566666
+          -0.048296 0.117674 -0.182667 0.974912
+          0.000006 -0.242274 0.000003
+          2.599999
+          -0.066365 0.114740 -0.176814 0.975278
+          0.000007 -0.242273 0.000002
+          2.633332
+          -0.067344 0.115229 -0.172908 0.975854
+          0.000006 -0.242273 0.000002
+          2.666666
+          -0.069300 0.115719 -0.164120 0.977175
+          0.000007 -0.242274 0.000002
+          2.699999
+          -0.066372 0.116698 -0.161189 0.977750
+          0.000007 -0.242273 0.000003
+          2.733332
+          -0.042933 0.119633 -0.169969 0.977218
+          0.000007 -0.242273 0.000002
+          2.766665
+          -0.005087 0.125501 -0.185092 0.974661
+          0.000006 -0.242273 0.000002
+          2.799999
+      "R_Clavicle_sjnt_0"
+      85
+          -0.144520 -0.139575 0.571095 0.795917
+          -0.007299 0.208857 0.030677
+          0.000000
+          -0.144033 -0.143482 0.575003 0.792489
+          -0.007299 0.208857 0.030677
+          0.033333
+          -0.143058 -0.148366 0.582818 0.786031
+          -0.007299 0.208857 0.030677
+          0.066667
+          -0.142084 -0.153739 0.591611 0.778571
+          -0.007299 0.208857 0.030677
+          0.100000
+          -0.142087 -0.159112 0.599426 0.771481
+          -0.007299 0.208857 0.030677
+          0.133333
+          -0.143553 -0.164483 0.601380 0.768557
+          -0.007299 0.208857 0.030677
+          0.166667
+          -0.146971 -0.170343 0.599424 0.768162
+          -0.007299 0.208857 0.030677
+          0.200000
+          -0.148436 -0.174737 0.597958 0.768037
+          -0.007299 0.208857 0.030677
+          0.233333
+          -0.147948 -0.175714 0.599424 0.766764
+          -0.007299 0.208857 0.030677
+          0.266667
+          -0.147948 -0.173273 0.601378 0.765789
+          -0.007299 0.208857 0.030677
+          0.300000
+          -0.148924 -0.168877 0.598447 0.768872
+          -0.007299 0.208857 0.030677
+          0.333333
+          -0.149410 -0.165458 0.592586 0.774043
+          -0.007299 0.208857 0.030677
+          0.366667
+          -0.148432 -0.163992 0.588189 0.777887
+          -0.007299 0.208857 0.030677
+          0.400000
+          -0.146235 -0.162040 0.587949 0.778893
+          -0.007299 0.208857 0.030677
+          0.433333
+          -0.144037 -0.160086 0.587702 0.779893
+          -0.007299 0.208857 0.030677
+          0.466667
+          -0.144036 -0.157155 0.582329 0.784506
+          -0.007299 0.208857 0.030677
+          0.500000
+          -0.144779 -0.153505 0.572123 0.792560
+          -0.007299 0.208857 0.030677
+          0.533333
+          -0.145495 -0.149827 0.561812 0.800469
+          -0.007299 0.208857 0.030677
+          0.566667
+          -0.145493 -0.145431 0.556439 0.805020
+          -0.007299 0.208857 0.030677
+          0.600000
+          -0.145004 -0.140060 0.553998 0.807740
+          -0.007299 0.208857 0.030677
+          0.633333
+          -0.145492 -0.136642 0.553998 0.808237
+          -0.007299 0.208857 0.030677
+          0.666667
+          -0.147205 -0.135668 0.558403 0.805053
+          -0.007299 0.208857 0.030677
+          0.700000
+          -0.148912 -0.134690 0.562790 0.801842
+          -0.007299 0.208857 0.030677
+          0.733334
+          -0.148912 -0.133714 0.564256 0.800975
+          -0.007299 0.208857 0.030677
+          0.766667
+          -0.148424 -0.132737 0.562791 0.802258
+          -0.007299 0.208857 0.030677
+          0.800000
+          -0.148912 -0.132736 0.561325 0.803194
+          -0.007299 0.208857 0.030677
+          0.833334
+          -0.148912 -0.134690 0.563767 0.801156
+          -0.007299 0.208857 0.030677
+          0.866667
+          -0.146962 -0.137134 0.567440 0.798504
+          -0.007299 0.208857 0.030677
+          0.900000
+          -0.145008 -0.139575 0.571095 0.795828
+          -0.007299 0.208857 0.030677
+          0.933334
+          -0.143545 -0.143482 0.575491 0.792224
+          -0.007299 0.208857 0.030677
+          0.966667
+          -0.143058 -0.148366 0.582330 0.786393
+          -0.007299 0.208857 0.030677
+          1.000000
+          -0.142084 -0.153739 0.591611 0.778571
+          -0.007299 0.208857 0.030677
+          1.033334
+          -0.142087 -0.159112 0.599427 0.771481
+          -0.007299 0.208857 0.030677
+          1.066667
+          -0.143553 -0.164484 0.601380 0.768557
+          -0.007299 0.208857 0.030677
+          1.100000
+          -0.146971 -0.170342 0.599425 0.768162
+          -0.007299 0.208857 0.030677
+          1.133334
+          -0.148436 -0.174737 0.597958 0.768036
+          -0.007299 0.208857 0.030677
+          1.166667
+          -0.147948 -0.175714 0.599424 0.766764
+          -0.007299 0.208857 0.030677
+          1.200000
+          -0.147948 -0.173273 0.601378 0.765789
+          -0.007299 0.208857 0.030677
+          1.233333
+          -0.148924 -0.168877 0.598447 0.768872
+          -0.007299 0.208857 0.030677
+          1.266667
+          -0.149410 -0.165458 0.592586 0.774043
+          -0.007299 0.208857 0.030677
+          1.300000
+          -0.148432 -0.163992 0.588189 0.777887
+          -0.007299 0.208857 0.030677
+          1.333333
+          -0.146235 -0.162040 0.587949 0.778893
+          -0.007299 0.208857 0.030677
+          1.366667
+          -0.144037 -0.160086 0.587702 0.779893
+          -0.007299 0.208857 0.030677
+          1.400000
+          -0.144036 -0.157155 0.582329 0.784506
+          -0.007299 0.208857 0.030677
+          1.433333
+          -0.144779 -0.153505 0.572123 0.792560
+          -0.007299 0.208857 0.030677
+          1.466667
+          -0.145495 -0.149827 0.561812 0.800469
+          -0.007299 0.208857 0.030677
+          1.500000
+          -0.145493 -0.145431 0.556439 0.805020
+          -0.007299 0.208857 0.030677
+          1.533333
+          -0.145004 -0.140060 0.553998 0.807740
+          -0.007299 0.208857 0.030677
+          1.566666
+          -0.145492 -0.136642 0.553998 0.808237
+          -0.007299 0.208857 0.030677
+          1.600000
+          -0.147205 -0.135668 0.558403 0.805054
+          -0.007299 0.208857 0.030677
+          1.633333
+          -0.148912 -0.134690 0.562790 0.801842
+          -0.007299 0.208857 0.030677
+          1.666666
+          -0.148912 -0.133714 0.564256 0.800975
+          -0.007299 0.208857 0.030677
+          1.700000
+          -0.148424 -0.132737 0.562791 0.802258
+          -0.007299 0.208857 0.030677
+          1.733333
+          -0.148912 -0.132736 0.561325 0.803194
+          -0.007299 0.208857 0.030677
+          1.766666
+          -0.148912 -0.134690 0.563767 0.801156
+          -0.007299 0.208857 0.030677
+          1.800000
+          -0.146963 -0.137134 0.567440 0.798504
+          -0.007299 0.208857 0.030677
+          1.833333
+          -0.145008 -0.139575 0.571095 0.795828
+          -0.007299 0.208858 0.030677
+          1.866666
+          -0.143545 -0.143482 0.575491 0.792224
+          -0.007299 0.208857 0.030677
+          1.899999
+          -0.143058 -0.148366 0.582330 0.786393
+          -0.007299 0.208857 0.030677
+          1.933333
+          -0.142084 -0.153739 0.591611 0.778571
+          -0.007299 0.208857 0.030677
+          1.966666
+          -0.142087 -0.159112 0.599426 0.771481
+          -0.007299 0.208857 0.030677
+          1.999999
+          -0.143553 -0.164483 0.601379 0.768558
+          -0.007299 0.208857 0.030677
+          2.033333
+          -0.146971 -0.170342 0.599425 0.768162
+          -0.007299 0.208857 0.030677
+          2.066666
+          -0.148436 -0.174737 0.597958 0.768037
+          -0.007299 0.208857 0.030677
+          2.099999
+          -0.147948 -0.175714 0.599424 0.766764
+          -0.007299 0.208857 0.030677
+          2.133333
+          -0.147948 -0.173273 0.601378 0.765789
+          -0.007299 0.208858 0.030676
+          2.166666
+          -0.148923 -0.168877 0.598447 0.768871
+          -0.007299 0.208857 0.030677
+          2.199999
+          -0.149410 -0.165458 0.592586 0.774043
+          -0.007299 0.208857 0.030677
+          2.233333
+          -0.148432 -0.163992 0.588190 0.777887
+          -0.007299 0.208857 0.030677
+          2.266666
+          -0.146235 -0.162040 0.587949 0.778893
+          -0.007299 0.208857 0.030677
+          2.299999
+          -0.144037 -0.160086 0.587702 0.779893
+          -0.007299 0.208857 0.030677
+          2.333333
+          -0.144036 -0.157155 0.582329 0.784506
+          -0.007299 0.208857 0.030678
+          2.366666
+          -0.144779 -0.153505 0.572123 0.792560
+          -0.007299 0.208857 0.030677
+          2.399999
+          -0.145495 -0.149827 0.561813 0.800469
+          -0.007299 0.208858 0.030677
+          2.433332
+          -0.145493 -0.145431 0.556440 0.805020
+          -0.007299 0.208857 0.030677
+          2.466666
+          -0.145004 -0.140060 0.553998 0.807740
+          -0.007299 0.208858 0.030677
+          2.499999
+          -0.145492 -0.136642 0.553998 0.808237
+          -0.007299 0.208857 0.030677
+          2.533332
+          -0.147446 -0.135177 0.557905 0.805437
+          -0.007299 0.208857 0.030677
+          2.566666
+          -0.148912 -0.134690 0.562790 0.801843
+          -0.007299 0.208857 0.030677
+          2.599999
+          -0.148913 -0.133714 0.564744 0.800631
+          -0.007299 0.208857 0.030677
+          2.633332
+          -0.148424 -0.132737 0.562791 0.802258
+          -0.007299 0.208857 0.030677
+          2.666666
+          -0.148912 -0.132736 0.561325 0.803194
+          -0.007299 0.208857 0.030677
+          2.699999
+          -0.148912 -0.134690 0.563767 0.801156
+          -0.007299 0.208857 0.030678
+          2.733332
+          -0.147449 -0.137132 0.568164 0.797900
+          -0.007299 0.208857 0.030677
+          2.766665
+          -0.144520 -0.139574 0.571095 0.795917
+          -0.007299 0.208857 0.030677
+          2.799999
+      "R_Arm_sjnt_0"
+      85
+          -0.430304 0.240672 -0.559139 -0.666542
+          -0.070457 0.150703 -0.005819
+          0.000000
+          0.416144 -0.257283 0.545462 0.680515
+          -0.070457 0.150703 -0.005820
+          0.033333
+          0.395146 -0.271940 0.532025 0.697752
+          -0.070458 0.150703 -0.005820
+          0.066667
+          0.370238 -0.281221 0.522249 0.714909
+          -0.070457 0.150703 -0.005820
+          0.100000
+          0.348258 -0.283663 0.517845 0.728071
+          -0.070457 0.150703 -0.005820
+          0.133333
+          0.338978 -0.279265 0.519061 0.733267
+          -0.070458 0.150703 -0.005820
+          0.166667
+          0.339464 -0.270959 0.525164 0.731811
+          -0.070458 0.150703 -0.005820
+          0.200000
+          0.341904 -0.259722 0.532000 0.729809
+          -0.070458 0.150703 -0.005819
+          0.233333
+          0.342880 -0.250928 0.534929 0.730287
+          -0.070457 0.150703 -0.005820
+          0.266667
+          0.345324 -0.246043 0.534440 0.731156
+          -0.070457 0.150703 -0.005820
+          0.300000
+          0.353139 -0.243112 0.535663 0.727498
+          -0.070458 0.150703 -0.005820
+          0.333333
+          0.364861 -0.235784 0.539574 0.721209
+          -0.070457 0.150703 -0.005820
+          0.366667
+          -0.376584 0.223572 -0.542262 -0.717044
+          -0.070457 0.150703 -0.005820
+          0.400000
+          -0.387821 0.208428 -0.542262 -0.715615
+          -0.070458 0.150703 -0.005820
+          0.433333
+          -0.400522 0.192307 -0.543484 -0.712198
+          -0.070458 0.150703 -0.005820
+          0.466667
+          -0.416640 0.175209 -0.549103 -0.702993
+          -0.070458 0.150703 -0.005820
+          0.500000
+          -0.435199 0.157623 -0.555700 -0.690619
+          -0.070458 0.150703 -0.005820
+          0.533333
+          -0.452296 0.140526 -0.557900 -0.681490
+          -0.070458 0.150703 -0.005820
+          0.566667
+          -0.464509 0.124894 -0.555948 -0.677904
+          -0.070458 0.150703 -0.005820
+          0.600000
+          -0.470861 0.113659 -0.554727 -0.676499
+          -0.070457 0.150703 -0.005820
+          0.633333
+          -0.472326 0.109262 -0.556191 -0.674997
+          -0.070458 0.150703 -0.005819
+          0.666667
+          -0.469393 0.114635 -0.558145 -0.674539
+          -0.070457 0.150703 -0.005820
+          0.700000
+          -0.462552 0.128801 -0.561077 -0.674276
+          -0.070458 0.150703 -0.005820
+          0.733334
+          -0.453267 0.147852 -0.567183 -0.671560
+          -0.070458 0.150703 -0.005820
+          0.766667
+          -0.447400 0.169345 -0.574269 -0.664358
+          -0.070457 0.150703 -0.005820
+          0.800000
+          -0.446419 0.190352 -0.577204 -0.656743
+          -0.070458 0.150703 -0.005819
+          0.833334
+          -0.445930 0.209405 -0.574523 -0.653620
+          -0.070457 0.150703 -0.005820
+          0.866667
+          -0.441536 0.225527 -0.567931 -0.656991
+          -0.070458 0.150703 -0.005820
+          0.900000
+          -0.431282 0.240672 -0.558162 -0.666729
+          -0.070458 0.150703 -0.005820
+          0.933334
+          0.416633 -0.257283 0.545462 0.680216
+          -0.070457 0.150703 -0.005820
+          0.966667
+          0.395634 -0.272428 0.532270 0.697098
+          -0.070457 0.150703 -0.005819
+          1.000000
+          0.369749 -0.281710 0.522249 0.714969
+          -0.070457 0.150703 -0.005820
+          1.033334
+          0.348258 -0.283663 0.517845 0.728071
+          -0.070457 0.150703 -0.005820
+          1.066667
+          0.338977 -0.279265 0.519305 0.733094
+          -0.070458 0.150703 -0.005819
+          1.100000
+          0.339464 -0.270959 0.525164 0.731811
+          -0.070457 0.150703 -0.005820
+          1.133334
+          0.341904 -0.259722 0.532000 0.729810
+          -0.070457 0.150703 -0.005820
+          1.166667
+          0.342881 -0.250928 0.534685 0.730466
+          -0.070457 0.150703 -0.005820
+          1.200000
+          0.345324 -0.246043 0.534440 0.731156
+          -0.070457 0.150703 -0.005820
+          1.233333
+          0.353139 -0.243112 0.535663 0.727498
+          -0.070457 0.150703 -0.005820
+          1.266667
+          0.364861 -0.235784 0.539574 0.721209
+          -0.070458 0.150703 -0.005820
+          1.300000
+          -0.376584 0.223572 -0.542262 -0.717044
+          -0.070457 0.150703 -0.005820
+          1.333333
+          -0.387821 0.208428 -0.542262 -0.715615
+          -0.070457 0.150703 -0.005820
+          1.366667
+          -0.400522 0.192307 -0.543484 -0.712198
+          -0.070457 0.150703 -0.005820
+          1.400000
+          -0.416640 0.175209 -0.549103 -0.702993
+          -0.070457 0.150703 -0.005820
+          1.433333
+          -0.435199 0.157623 -0.555700 -0.690619
+          -0.070458 0.150703 -0.005820
+          1.466667
+          -0.452295 0.140526 -0.557901 -0.681490
+          -0.070457 0.150703 -0.005820
+          1.500000
+          -0.464509 0.124894 -0.555948 -0.677904
+          -0.070457 0.150703 -0.005820
+          1.533333
+          -0.470861 0.113659 -0.554727 -0.676499
+          -0.070457 0.150703 -0.005820
+          1.566666
+          -0.472326 0.109262 -0.556191 -0.674997
+          -0.070457 0.150703 -0.005820
+          1.600000
+          -0.469393 0.114635 -0.558145 -0.674539
+          -0.070458 0.150703 -0.005820
+          1.633333
+          -0.462553 0.128801 -0.561077 -0.674276
+          -0.070458 0.150703 -0.005820
+          1.666666
+          -0.453267 0.147852 -0.567183 -0.671560
+          -0.070457 0.150703 -0.005819
+          1.700000
+          -0.447400 0.169345 -0.574268 -0.664358
+          -0.070457 0.150703 -0.005820
+          1.733333
+          -0.446419 0.190351 -0.577204 -0.656743
+          -0.070457 0.150703 -0.005819
+          1.766666
+          -0.445930 0.209404 -0.574523 -0.653620
+          -0.070457 0.150703 -0.005820
+          1.800000
+          -0.441536 0.225527 -0.567932 -0.656991
+          -0.070457 0.150703 -0.005820
+          1.833333
+          -0.431282 0.240672 -0.558163 -0.666729
+          -0.070457 0.150703 -0.005820
+          1.866666
+          0.416633 -0.257283 0.545462 0.680216
+          -0.070457 0.150703 -0.005820
+          1.899999
+          0.395634 -0.272428 0.532270 0.697098
+          -0.070457 0.150703 -0.005820
+          1.933333
+          0.369750 -0.281710 0.522249 0.714969
+          -0.070457 0.150703 -0.005820
+          1.966666
+          0.348259 -0.283663 0.517845 0.728071
+          -0.070458 0.150703 -0.005820
+          1.999999
+          0.338977 -0.279266 0.519305 0.733094
+          -0.070458 0.150703 -0.005819
+          2.033333
+          0.339464 -0.270959 0.525164 0.731811
+          -0.070458 0.150702 -0.005820
+          2.066666
+          0.341904 -0.259722 0.532000 0.729809
+          -0.070458 0.150703 -0.005819
+          2.099999
+          0.342881 -0.250928 0.534685 0.730466
+          -0.070458 0.150703 -0.005820
+          2.133333
+          0.345324 -0.246043 0.534440 0.731156
+          -0.070457 0.150702 -0.005819
+          2.166666
+          0.353139 -0.243112 0.535663 0.727499
+          -0.070457 0.150703 -0.005820
+          2.199999
+          0.364861 -0.235784 0.539574 0.721209
+          -0.070457 0.150703 -0.005820
+          2.233333
+          -0.376584 0.223572 -0.542262 -0.717044
+          -0.070458 0.150702 -0.005820
+          2.266666
+          -0.387820 0.208428 -0.542262 -0.715615
+          -0.070457 0.150703 -0.005820
+          2.299999
+          -0.400521 0.192308 -0.543484 -0.712198
+          -0.070457 0.150702 -0.005820
+          2.333333
+          -0.416639 0.175210 -0.549103 -0.702993
+          -0.070458 0.150703 -0.005820
+          2.366666
+          -0.435199 0.157623 -0.555700 -0.690619
+          -0.070458 0.150703 -0.005820
+          2.399999
+          -0.452295 0.140526 -0.557900 -0.681490
+          -0.070458 0.150702 -0.005820
+          2.433332
+          -0.464997 0.124895 -0.555948 -0.677570
+          -0.070457 0.150702 -0.005820
+          2.466666
+          -0.470861 0.113659 -0.554727 -0.676499
+          -0.070458 0.150703 -0.005820
+          2.499999
+          -0.472326 0.109262 -0.556191 -0.674997
+          -0.070457 0.150702 -0.005820
+          2.533332
+          -0.469394 0.114635 -0.558145 -0.674539
+          -0.070457 0.150703 -0.005820
+          2.566666
+          -0.462553 0.128801 -0.561077 -0.674276
+          -0.070458 0.150703 -0.005820
+          2.599999
+          -0.453267 0.147851 -0.567183 -0.671560
+          -0.070457 0.150702 -0.005820
+          2.633332
+          -0.447400 0.169345 -0.574268 -0.664358
+          -0.070457 0.150703 -0.005820
+          2.666666
+          -0.445931 0.190351 -0.577448 -0.656861
+          -0.070457 0.150703 -0.005820
+          2.699999
+          -0.445930 0.208916 -0.574278 -0.653991
+          -0.070457 0.150703 -0.005820
+          2.733332
+          -0.441536 0.225526 -0.567443 -0.657413
+          -0.070458 0.150703 -0.005820
+          2.766665
+          -0.430305 0.240671 -0.559139 -0.666542
+          -0.070457 0.150702 -0.005819
+          2.799999
+      "R_Arm_sjnt_1"
+      85
+          0.277522 -0.019081 -0.097148 0.955604
+          -0.000001 0.260632 -0.000000
+          0.000000
+          -0.281427 0.003935 0.093736 -0.954985
+          -0.000001 0.260632 -0.000000
+          0.033333
+          -0.293147 -0.016098 0.089345 -0.951747
+          -0.000001 0.260632 -0.000000
+          0.066667
+          -0.303893 -0.023428 0.088366 -0.948310
+          -0.000001 0.260632 -0.000000
+          0.100000
+          -0.305359 -0.022451 0.088365 -0.947863
+          -0.000001 0.260632 -0.000000
+          0.133333
+          -0.288748 -0.025380 0.086421 -0.953159
+          -0.000001 0.260632 -0.000000
+          0.166667
+          -0.257480 -0.032705 0.083020 -0.962155
+          -0.000001 0.260632 -0.000000
+          0.200000
+          -0.223771 -0.034655 0.080595 -0.970685
+          -0.000001 0.260632 -0.000000
+          0.233333
+          -0.202277 -0.029767 0.080602 -0.975552
+          -0.000001 0.260632 -0.000000
+          0.266667
+          -0.196904 -0.023415 0.081090 -0.976783
+          -0.000001 0.260632 -0.000000
+          0.300000
+          -0.201302 -0.019507 0.082063 -0.975891
+          -0.000001 0.260632 -0.000000
+          0.333333
+          -0.210584 -0.019508 0.083035 -0.973848
+          -0.000001 0.260632 -0.000000
+          0.366667
+          0.225727 0.025373 -0.082543 0.970356
+          -0.000001 0.260632 -0.000000
+          0.400000
+          0.247708 0.038078 -0.081074 0.964685
+          -0.000001 0.260632 -0.000000
+          0.433333
+          0.274085 0.050784 -0.079115 0.957099
+          -0.000001 0.260632 -0.000000
+          0.466667
+          0.300953 0.055185 -0.079104 0.948749
+          -0.000001 0.260632 -0.000000
+          0.500000
+          0.330266 0.051769 -0.081043 0.938976
+          -0.000001 0.260632 -0.000000
+          0.533333
+          0.362021 0.049330 -0.083469 0.927114
+          -0.000001 0.260632 -0.000000
+          0.566667
+          0.392309 0.050800 -0.083943 0.914585
+          -0.000001 0.260632 -0.000000
+          0.600000
+          0.415270 0.053246 -0.084422 0.904206
+          -0.000001 0.260632 -0.000000
+          0.633333
+          0.426994 0.054225 -0.084417 0.898671
+          -0.000001 0.260633 -0.000000
+          0.666667
+          0.428459 0.057157 -0.083441 0.897883
+          -0.000001 0.260632 -0.000000
+          0.700000
+          0.420152 0.063019 -0.080030 0.901719
+          -0.000001 0.260632 -0.000000
+          0.733334
+          0.400120 0.068391 -0.077112 0.910648
+          -0.000001 0.260632 -0.000000
+          0.766667
+          0.370320 0.069852 -0.076150 0.923139
+          -0.000001 0.260632 -0.000000
+          0.800000
+          0.335637 0.061053 -0.078115 0.936759
+          -0.000001 0.260632 -0.000000
+          0.833334
+          0.304378 0.037596 -0.083978 0.948097
+          -0.000001 0.260633 -0.000001
+          0.866667
+          0.283867 0.005836 -0.091297 0.954490
+          -0.000001 0.260632 -0.000000
+          0.900000
+          0.276544 -0.013219 -0.095687 0.956134
+          -0.000001 0.260632 -0.000000
+          0.933334
+          -0.280939 0.005889 0.094223 -0.955071
+          -0.000001 0.260633 -0.000000
+          0.966667
+          -0.292659 -0.013655 0.089832 -0.951890
+          -0.000001 0.260632 -0.000001
+          1.000000
+          -0.304381 -0.023916 0.087878 -0.948187
+          -0.000001 0.260632 -0.000000
+          1.033334
+          -0.305358 -0.023428 0.088366 -0.947839
+          -0.000001 0.260633 -0.000000
+          1.066667
+          -0.288747 -0.025380 0.086421 -0.953159
+          -0.000001 0.260632 -0.000000
+          1.100000
+          -0.256992 -0.032216 0.083020 -0.962302
+          -0.000001 0.260633 -0.000000
+          1.133334
+          -0.223771 -0.034655 0.080595 -0.970685
+          -0.000001 0.260632 -0.000000
+          1.166667
+          -0.202277 -0.029767 0.080602 -0.975552
+          -0.000001 0.260632 -0.000000
+          1.200000
+          -0.196904 -0.023415 0.081090 -0.976783
+          -0.000001 0.260632 -0.000000
+          1.233333
+          -0.201302 -0.019507 0.082063 -0.975891
+          -0.000001 0.260632 -0.000001
+          1.266667
+          -0.210584 -0.019508 0.083035 -0.973848
+          -0.000001 0.260633 -0.000000
+          1.300000
+          0.225727 0.025373 -0.082543 0.970356
+          -0.000001 0.260632 -0.000000
+          1.333333
+          0.247708 0.038078 -0.081074 0.964685
+          -0.000001 0.260633 -0.000000
+          1.366667
+          0.274085 0.050784 -0.079115 0.957099
+          -0.000001 0.260633 -0.000000
+          1.400000
+          0.300953 0.055185 -0.079104 0.948749
+          -0.000001 0.260632 -0.000000
+          1.433333
+          0.330266 0.051769 -0.081043 0.938976
+          -0.000000 0.260632 -0.000000
+          1.466667
+          0.362021 0.049330 -0.083468 0.927114
+          -0.000001 0.260632 -0.000001
+          1.500000
+          0.392309 0.050800 -0.083943 0.914585
+          -0.000001 0.260632 0.000000
+          1.533333
+          0.415269 0.053246 -0.084422 0.904206
+          -0.000001 0.260632 -0.000000
+          1.566666
+          0.426994 0.054225 -0.084417 0.898671
+          -0.000001 0.260632 -0.000001
+          1.600000
+          0.428459 0.057157 -0.083441 0.897883
+          -0.000001 0.260632 0.000000
+          1.633333
+          0.420152 0.063019 -0.080030 0.901719
+          -0.000001 0.260632 0.000000
+          1.666666
+          0.400121 0.068391 -0.077112 0.910648
+          -0.000001 0.260632 -0.000000
+          1.700000
+          0.370321 0.069852 -0.076149 0.923138
+          -0.000001 0.260633 -0.000001
+          1.733333
+          0.335638 0.061053 -0.078115 0.936759
+          -0.000001 0.260633 -0.000000
+          1.766666
+          0.304378 0.037597 -0.083978 0.948097
+          -0.000001 0.260632 -0.000000
+          1.800000
+          0.283868 0.005837 -0.091297 0.954489
+          -0.000001 0.260632 -0.000000
+          1.833333
+          0.276544 -0.013218 -0.095687 0.956134
+          -0.000001 0.260632 -0.000000
+          1.866666
+          -0.280939 0.005890 0.094223 -0.955071
+          -0.000001 0.260632 -0.000000
+          1.899999
+          -0.292659 -0.013654 0.089832 -0.951890
+          -0.000001 0.260632 -0.000000
+          1.933333
+          -0.304381 -0.023916 0.087878 -0.948187
+          -0.000001 0.260632 -0.000000
+          1.966666
+          -0.305358 -0.023428 0.088366 -0.947839
+          -0.000001 0.260632 -0.000000
+          1.999999
+          -0.288748 -0.025380 0.086421 -0.953159
+          -0.000001 0.260632 -0.000000
+          2.033333
+          -0.256992 -0.032216 0.083020 -0.962302
+          -0.000001 0.260631 0.000000
+          2.066666
+          -0.223772 -0.034655 0.080595 -0.970685
+          -0.000001 0.260632 0.000000
+          2.099999
+          -0.202277 -0.029767 0.080602 -0.975552
+          -0.000001 0.260632 -0.000000
+          2.133333
+          -0.196904 -0.023415 0.081090 -0.976783
+          -0.000001 0.260633 -0.000000
+          2.166666
+          -0.201302 -0.019507 0.082063 -0.975891
+          -0.000001 0.260632 -0.000000
+          2.199999
+          -0.210584 -0.019508 0.083035 -0.973848
+          -0.000001 0.260632 -0.000000
+          2.233333
+          0.225727 0.025372 -0.082543 0.970356
+          -0.000001 0.260632 -0.000000
+          2.266666
+          0.247707 0.038078 -0.081074 0.964686
+          -0.000001 0.260632 -0.000001
+          2.299999
+          0.274085 0.050784 -0.079115 0.957099
+          -0.000001 0.260632 -0.000000
+          2.333333
+          0.300953 0.055185 -0.079104 0.948749
+          -0.000000 0.260632 0.000000
+          2.366666
+          0.330265 0.051769 -0.081043 0.938977
+          -0.000000 0.260632 0.000000
+          2.399999
+          0.362020 0.049330 -0.083468 0.927114
+          -0.000000 0.260632 -0.000000
+          2.433332
+          0.392308 0.050800 -0.083943 0.914586
+          -0.000001 0.260633 -0.000000
+          2.466666
+          0.415269 0.053246 -0.084422 0.904206
+          -0.000001 0.260632 0.000000
+          2.499999
+          0.426993 0.054225 -0.084417 0.898671
+          -0.000001 0.260633 0.000000
+          2.533332
+          0.428459 0.057156 -0.083441 0.897883
+          -0.000001 0.260633 -0.000000
+          2.566666
+          0.420152 0.063019 -0.080030 0.901718
+          -0.000000 0.260633 0.000000
+          2.599999
+          0.400121 0.068391 -0.077112 0.910648
+          -0.000001 0.260633 -0.000000
+          2.633332
+          0.369833 0.068875 -0.076149 0.923407
+          -0.000001 0.260633 -0.000000
+          2.666666
+          0.335150 0.060076 -0.078603 0.936956
+          -0.000001 0.260633 0.000000
+          2.699999
+          0.304867 0.039552 -0.083490 0.947904
+          -0.000001 0.260632 -0.000000
+          2.733332
+          0.283867 0.010235 -0.090322 0.954545
+          -0.000001 0.260633 -0.000001
+          2.766665
+          0.277523 -0.019080 -0.097148 0.955604
+          -0.000001 0.260633 0.000000
+          2.799999
+      "R_Wrist_sjnt_0"
+      85
+          0.000456 0.025368 -0.050792 0.998387
+          0.000000 0.242268 -0.000001
+          0.000000
+          0.004038 0.026350 -0.040293 0.998832
+          0.000000 0.242268 -0.000001
+          0.033333
+          0.002266 0.026352 -0.035411 0.999023
+          0.000000 0.242268 -0.000001
+          0.066667
+          -0.000340 0.025859 -0.043713 0.998709
+          0.000000 0.242268 -0.000001
+          0.100000
+          -0.003524 0.024876 -0.056897 0.998064
+          0.000000 0.242268 -0.000001
+          0.133333
+          0.006124 0.024873 -0.069345 0.997264
+          0.000000 0.242268 -0.000001
+          0.166667
+          -0.002540 0.023892 -0.075695 0.996842
+          0.000000 0.242268 -0.000001
+          0.200000
+          -0.006077 0.022911 -0.083509 0.996225
+          0.000000 0.242268 -0.000001
+          0.233333
+          -0.019507 0.022423 -0.075213 0.996724
+          0.000000 0.242268 -0.000000
+          0.266667
+          -0.041976 0.022425 -0.056911 0.997244
+          0.000000 0.242268 -0.000001
+          0.300000
+          -0.046864 0.023407 -0.043242 0.997690
+          0.000000 0.242268 -0.000000
+          0.333333
+          -0.040029 0.023898 -0.041042 0.998069
+          0.000000 0.242268 -0.000000
+          0.366667
+          -0.027822 0.023900 -0.048977 0.998126
+          0.000000 0.242268 -0.000001
+          0.400000
+          -0.015609 0.023897 -0.056901 0.997972
+          0.000000 0.242268 -0.000001
+          0.433333
+          -0.009993 0.024385 -0.059097 0.997904
+          0.000000 0.242268 -0.000001
+          0.466667
+          -0.003213 0.024382 -0.070813 0.997186
+          0.000000 0.242268 -0.000001
+          0.500000
+          0.006740 0.024379 -0.082039 0.996308
+          0.000000 0.242268 -0.000000
+          0.533333
+          -0.000783 0.023890 -0.080577 0.996462
+          0.000000 0.242268 -0.000000
+          0.566667
+          -0.008523 0.023403 -0.072279 0.997073
+          0.000000 0.242268 -0.000000
+          0.600000
+          0.006736 0.024872 -0.072762 0.997016
+          0.000000 0.242268 -0.000000
+          0.633333
+          0.030789 0.026336 -0.087402 0.995349
+          0.000000 0.242268 -0.000000
+          0.666667
+          0.038118 0.026820 -0.100583 0.993836
+          0.000000 0.242268 -0.000000
+          0.700000
+          0.022984 0.025350 -0.104494 0.993937
+          0.000000 0.242268 -0.000001
+          0.733334
+          0.021032 0.024860 -0.107425 0.993680
+          0.000000 0.242268 -0.000000
+          0.766667
+          0.022497 0.024860 -0.108401 0.993542
+          0.000000 0.242268 -0.000000
+          0.800000
+          0.015411 0.024865 -0.094243 0.995119
+          0.000000 0.242268 -0.000001
+          0.833334
+          0.011745 0.024868 -0.083502 0.996128
+          0.000000 0.242268 -0.000001
+          0.866667
+          0.008322 0.024873 -0.070808 0.997145
+          0.000000 0.242268 -0.000000
+          0.900000
+          0.000456 0.025368 -0.050792 0.998387
+          0.000000 0.242268 -0.000000
+          0.933334
+          0.004037 0.026350 -0.040293 0.998832
+          0.000000 0.242268 -0.000001
+          0.966667
+          0.002265 0.026352 -0.035411 0.999023
+          0.000000 0.242268 -0.000001
+          1.000000
+          -0.000340 0.025859 -0.043713 0.998709
+          0.000000 0.242268 -0.000001
+          1.033334
+          -0.003524 0.024876 -0.056897 0.998064
+          0.000000 0.242268 -0.000000
+          1.066667
+          0.006124 0.024873 -0.069345 0.997264
+          0.000000 0.242268 -0.000000
+          1.100000
+          -0.002540 0.023892 -0.075695 0.996842
+          0.000000 0.242268 -0.000000
+          1.133334
+          -0.006077 0.022911 -0.083509 0.996225
+          0.000000 0.242268 -0.000001
+          1.166667
+          -0.019508 0.022423 -0.075213 0.996724
+          0.000000 0.242268 -0.000001
+          1.200000
+          -0.041976 0.022425 -0.056911 0.997244
+          0.000000 0.242268 -0.000000
+          1.233333
+          -0.046864 0.023407 -0.043242 0.997690
+          0.000000 0.242268 -0.000000
+          1.266667
+          -0.040029 0.023898 -0.041042 0.998069
+          0.000000 0.242268 -0.000000
+          1.300000
+          -0.027822 0.023900 -0.048977 0.998126
+          0.000000 0.242268 -0.000001
+          1.333333
+          -0.015609 0.023897 -0.056901 0.997972
+          0.000000 0.242268 -0.000001
+          1.366667
+          -0.009993 0.024385 -0.059097 0.997904
+          0.000000 0.242268 -0.000000
+          1.400000
+          -0.003213 0.024382 -0.070813 0.997186
+          0.000000 0.242268 -0.000000
+          1.433333
+          0.006740 0.024379 -0.082039 0.996308
+          0.000000 0.242268 -0.000000
+          1.466667
+          -0.000783 0.023890 -0.080577 0.996462
+          0.000000 0.242268 -0.000000
+          1.500000
+          -0.008523 0.023403 -0.072279 0.997073
+          0.000000 0.242268 -0.000001
+          1.533333
+          0.006736 0.024872 -0.072762 0.997016
+          0.000000 0.242268 -0.000000
+          1.566666
+          0.030789 0.026336 -0.087402 0.995349
+          0.000000 0.242268 -0.000000
+          1.600000
+          0.038118 0.026821 -0.100583 0.993836
+          0.000000 0.242268 -0.000001
+          1.633333
+          0.022984 0.025350 -0.104494 0.993937
+          0.000000 0.242268 -0.000000
+          1.666666
+          0.021032 0.024860 -0.107425 0.993680
+          0.000000 0.242268 -0.000000
+          1.700000
+          0.022497 0.024860 -0.108401 0.993542
+          0.000000 0.242268 -0.000000
+          1.733333
+          0.015411 0.024865 -0.094244 0.995119
+          0.000000 0.242268 0.000000
+          1.766666
+          0.011745 0.024868 -0.083503 0.996128
+          0.000000 0.242268 -0.000000
+          1.800000
+          0.008322 0.024873 -0.070809 0.997145
+          0.000000 0.242268 -0.000000
+          1.833333
+          0.000456 0.025368 -0.050793 0.998387
+          0.000000 0.242268 -0.000001
+          1.866666
+          0.004037 0.026350 -0.040294 0.998832
+          0.000000 0.242269 -0.000001
+          1.899999
+          0.002265 0.026352 -0.035411 0.999023
+          0.000000 0.242268 -0.000000
+          1.933333
+          -0.000340 0.025860 -0.043713 0.998709
+          0.000000 0.242269 -0.000000
+          1.966666
+          -0.003524 0.024876 -0.056897 0.998064
+          0.000000 0.242268 -0.000000
+          1.999999
+          0.006124 0.024873 -0.069344 0.997264
+          0.000000 0.242269 -0.000001
+          2.033333
+          -0.002540 0.023892 -0.075695 0.996842
+          0.000000 0.242269 -0.000000
+          2.066666
+          -0.006077 0.022911 -0.083508 0.996225
+          0.000000 0.242268 -0.000000
+          2.099999
+          -0.019507 0.022423 -0.075213 0.996725
+          0.000000 0.242268 -0.000000
+          2.133333
+          -0.041975 0.022425 -0.056912 0.997244
+          0.000000 0.242268 -0.000000
+          2.166666
+          -0.046864 0.023407 -0.043242 0.997690
+          0.000000 0.242268 0.000000
+          2.199999
+          -0.040029 0.023898 -0.041042 0.998069
+          0.000000 0.242268 -0.000000
+          2.233333
+          -0.027822 0.023900 -0.048977 0.998126
+          0.000000 0.242268 -0.000000
+          2.266666
+          -0.015609 0.023897 -0.056901 0.997972
+          0.000000 0.242268 -0.000001
+          2.299999
+          -0.009993 0.024385 -0.059097 0.997904
+          0.000000 0.242268 -0.000001
+          2.333333
+          -0.003214 0.024382 -0.070812 0.997186
+          0.000000 0.242269 -0.000000
+          2.366666
+          0.006739 0.024379 -0.082039 0.996308
+          0.000000 0.242269 -0.000001
+          2.399999
+          -0.000783 0.023890 -0.080577 0.996462
+          0.000000 0.242268 -0.000001
+          2.433332
+          -0.008522 0.023403 -0.072280 0.997073
+          -0.000000 0.242269 -0.000001
+          2.466666
+          0.006736 0.024872 -0.072762 0.997016
+          0.000000 0.242269 -0.000001
+          2.499999
+          0.030788 0.026336 -0.087401 0.995349
+          0.000000 0.242268 -0.000000
+          2.533332
+          0.038118 0.026821 -0.100582 0.993836
+          0.000000 0.242268 -0.000000
+          2.566666
+          0.022984 0.025350 -0.104494 0.993937
+          0.000000 0.242269 0.000000
+          2.599999
+          0.021032 0.024860 -0.107425 0.993680
+          0.000000 0.242268 -0.000000
+          2.633332
+          0.022497 0.024860 -0.108401 0.993542
+          0.000000 0.242268 0.000000
+          2.666666
+          0.015412 0.024865 -0.094244 0.995119
+          0.000000 0.242268 0.000000
+          2.699999
+          0.011745 0.024868 -0.083503 0.996128
+          0.000000 0.242268 -0.000000
+          2.733332
+          0.008322 0.024873 -0.070809 0.997145
+          0.000000 0.242269 -0.000000
+          2.766665
+          0.000456 0.025368 -0.050793 0.998387
+          0.000000 0.242269 0.000000
+          2.799999
+      "C_Neck_sjnt_0"
+      85
+          0.184079 0.118660 -0.039556 0.974921
+          -0.000000 0.206349 -0.038118
+          0.000000
+          0.182859 0.118171 -0.036870 0.975315
+          0.000000 0.206349 -0.038118
+          0.033333
+          0.181637 0.117682 -0.034185 0.975700
+          -0.000000 0.206349 -0.038118
+          0.066667
+          0.181637 0.116217 -0.031744 0.975958
+          0.000000 0.206349 -0.038118
+          0.100000
+          0.182615 0.115240 -0.028204 0.976000
+          0.000000 0.206349 -0.038118
+          0.133333
+          0.183591 0.114262 -0.024664 0.976028
+          -0.000000 0.206349 -0.038118
+          0.166667
+          0.182615 0.112797 -0.023810 0.976402
+          -0.000000 0.206349 -0.038118
+          0.200000
+          0.181638 0.111332 -0.022956 0.976773
+          0.000000 0.206349 -0.038118
+          0.233333
+          0.183103 0.109379 -0.022712 0.976726
+          0.000000 0.206349 -0.038118
+          0.266667
+          0.184083 0.105231 -0.026864 0.976892
+          0.000000 0.206349 -0.038118
+          0.300000
+          0.185056 0.101080 -0.031015 0.977024
+          0.000000 0.206349 -0.038118
+          0.333333
+          0.187744 0.097420 -0.033091 0.976815
+          -0.000000 0.206349 -0.038118
+          0.366667
+          0.190427 0.093757 -0.035167 0.976581
+          0.000000 0.206349 -0.038118
+          0.400000
+          0.193845 0.091804 -0.035655 0.976076
+          -0.000000 0.206349 -0.038118
+          0.433333
+          0.193845 0.090828 -0.037120 0.976113
+          0.000000 0.206349 -0.038118
+          0.466667
+          0.191894 0.091480 -0.038911 0.976368
+          -0.000000 0.206349 -0.038118
+          0.500000
+          0.189940 0.092131 -0.040701 0.976616
+          0.000000 0.206349 -0.038118
+          0.533333
+          0.187986 0.092782 -0.042491 0.976856
+          0.000000 0.206349 -0.038118
+          0.566667
+          0.185544 0.093271 -0.045909 0.977121
+          0.000000 0.206349 -0.038118
+          0.600000
+          0.184568 0.093760 -0.049327 0.977093
+          0.000000 0.206349 -0.038118
+          0.633333
+          0.183103 0.095713 -0.050791 0.977104
+          0.000000 0.206349 -0.038118
+          0.666667
+          0.180418 0.098155 -0.049814 0.977412
+          -0.000000 0.206349 -0.038118
+          0.700000
+          0.177732 0.100595 -0.048837 0.977705
+          -0.000000 0.206349 -0.038118
+          0.733334
+          0.177490 0.103648 -0.048105 0.977466
+          0.000000 0.206349 -0.038118
+          0.766667
+          0.177247 0.106701 -0.047372 0.977218
+          0.000000 0.206349 -0.038118
+          0.800000
+          0.177002 0.109752 -0.046638 0.976959
+          -0.000000 0.206349 -0.038118
+          0.833334
+          0.176755 0.112802 -0.045905 0.976691
+          0.000000 0.206349 -0.038118
+          0.866667
+          0.179442 0.115244 -0.043219 0.976039
+          0.000000 0.206349 -0.038118
+          0.900000
+          0.182126 0.117683 -0.040533 0.975366
+          0.000000 0.206349 -0.038118
+          0.933334
+          0.181882 0.117683 -0.037603 0.975528
+          -0.000000 0.206349 -0.038118
+          0.966667
+          0.181637 0.117682 -0.034673 0.975683
+          0.000000 0.206349 -0.038118
+          1.000000
+          0.182126 0.116217 -0.031744 0.975867
+          -0.000000 0.206349 -0.038118
+          1.033334
+          0.183102 0.115727 -0.027349 0.975875
+          0.000000 0.206349 -0.038118
+          1.066667
+          0.183591 0.114262 -0.024664 0.976028
+          0.000000 0.206349 -0.038118
+          1.100000
+          0.182615 0.112797 -0.023810 0.976402
+          -0.000000 0.206349 -0.038118
+          1.133334
+          0.181638 0.111332 -0.022956 0.976773
+          0.000000 0.206349 -0.038118
+          1.166667
+          0.183103 0.109379 -0.022712 0.976726
+          0.000000 0.206349 -0.038118
+          1.200000
+          0.184083 0.105231 -0.026864 0.976892
+          0.000000 0.206349 -0.038118
+          1.233333
+          0.185056 0.101080 -0.031015 0.977024
+          0.000000 0.206349 -0.038118
+          1.266667
+          0.187744 0.097420 -0.033091 0.976815
+          0.000000 0.206349 -0.038118
+          1.300000
+          0.190427 0.093757 -0.035167 0.976581
+          0.000000 0.206349 -0.038118
+          1.333333
+          0.193845 0.091804 -0.035655 0.976076
+          -0.000000 0.206349 -0.038118
+          1.366667
+          0.193845 0.090828 -0.037120 0.976113
+          0.000000 0.206349 -0.038118
+          1.400000
+          0.191894 0.091480 -0.038911 0.976368
+          0.000000 0.206349 -0.038118
+          1.433333
+          0.189940 0.092131 -0.040701 0.976616
+          0.000000 0.206349 -0.038118
+          1.466667
+          0.187986 0.092782 -0.042491 0.976856
+          -0.000000 0.206349 -0.038118
+          1.500000
+          0.185545 0.093271 -0.045909 0.977121
+          0.000000 0.206349 -0.038118
+          1.533333
+          0.184568 0.093760 -0.049327 0.977093
+          -0.000000 0.206349 -0.038118
+          1.566666
+          0.183103 0.095713 -0.050791 0.977104
+          0.000000 0.206349 -0.038118
+          1.600000
+          0.180419 0.098155 -0.049814 0.977412
+          -0.000000 0.206349 -0.038118
+          1.633333
+          0.177732 0.100595 -0.048837 0.977705
+          0.000000 0.206349 -0.038118
+          1.666666
+          0.177490 0.103648 -0.048105 0.977466
+          0.000000 0.206349 -0.038118
+          1.700000
+          0.177247 0.106700 -0.047372 0.977218
+          0.000000 0.206349 -0.038118
+          1.733333
+          0.177002 0.109752 -0.046638 0.976959
+          0.000000 0.206349 -0.038118
+          1.766666
+          0.176755 0.112802 -0.045905 0.976691
+          0.000000 0.206349 -0.038118
+          1.800000
+          0.179442 0.115244 -0.043219 0.976039
+          0.000000 0.206349 -0.038118
+          1.833333
+          0.182126 0.117683 -0.040533 0.975366
+          -0.000000 0.206349 -0.038118
+          1.866666
+          0.181882 0.117683 -0.037603 0.975528
+          0.000000 0.206349 -0.038118
+          1.899999
+          0.181637 0.117682 -0.034673 0.975683
+          0.000000 0.206349 -0.038118
+          1.933333
+          0.182126 0.116217 -0.031744 0.975867
+          0.000000 0.206349 -0.038118
+          1.966666
+          0.183102 0.115727 -0.027349 0.975875
+          -0.000000 0.206349 -0.038118
+          1.999999
+          0.183591 0.114262 -0.024664 0.976028
+          0.000000 0.206349 -0.038118
+          2.033333
+          0.182615 0.112797 -0.023810 0.976402
+          0.000000 0.206349 -0.038118
+          2.066666
+          0.181638 0.111332 -0.022956 0.976773
+          0.000000 0.206349 -0.038118
+          2.099999
+          0.183103 0.109379 -0.022712 0.976726
+          0.000000 0.206349 -0.038118
+          2.133333
+          0.184083 0.105231 -0.026864 0.976892
+          0.000000 0.206349 -0.038118
+          2.166666
+          0.185056 0.101080 -0.031015 0.977024
+          0.000000 0.206349 -0.038118
+          2.199999
+          0.187744 0.097420 -0.033091 0.976815
+          -0.000000 0.206349 -0.038118
+          2.233333
+          0.190427 0.093757 -0.035167 0.976581
+          0.000000 0.206349 -0.038118
+          2.266666
+          0.193845 0.091804 -0.035655 0.976076
+          0.000000 0.206349 -0.038118
+          2.299999
+          0.193845 0.090828 -0.037120 0.976113
+          0.000000 0.206349 -0.038118
+          2.333333
+          0.191894 0.091480 -0.038911 0.976368
+          -0.000000 0.206349 -0.038118
+          2.366666
+          0.189940 0.092131 -0.040701 0.976616
+          0.000000 0.206349 -0.038118
+          2.399999
+          0.187986 0.092782 -0.042491 0.976856
+          0.000000 0.206350 -0.038118
+          2.433332
+          0.186278 0.093271 -0.045909 0.976982
+          0.000000 0.206349 -0.038118
+          2.466666
+          0.184568 0.093760 -0.049327 0.977093
+          0.000000 0.206349 -0.038118
+          2.499999
+          0.183103 0.095713 -0.050791 0.977104
+          0.000000 0.206349 -0.038118
+          2.533332
+          0.180419 0.098155 -0.049814 0.977411
+          0.000000 0.206349 -0.038118
+          2.566666
+          0.177732 0.100595 -0.048837 0.977705
+          -0.000000 0.206349 -0.038118
+          2.599999
+          0.177368 0.103648 -0.048227 0.977482
+          0.000000 0.206349 -0.038118
+          2.633332
+          0.177002 0.106700 -0.047616 0.977250
+          0.000000 0.206349 -0.038118
+          2.666666
+          0.176635 0.109752 -0.047005 0.977008
+          0.000000 0.206349 -0.038118
+          2.699999
+          0.176266 0.112802 -0.046393 0.976757
+          0.000000 0.206349 -0.038118
+          2.733332
+          0.180176 0.115733 -0.042975 0.975856
+          -0.000000 0.206349 -0.038118
+          2.766665
+          0.184079 0.118660 -0.039556 0.974921
+          0.000000 0.206349 -0.038118
+          2.799999
+      "C_Neck_sjnt_1"
+      85
+          -0.210451 0.017581 -0.011746 0.977376
+          0.000000 0.054001 0.000000
+          0.000000
+          -0.210777 0.016116 -0.011420 0.977335
+          -0.000000 0.054001 0.000000
+          0.033333
+          -0.211103 0.014651 -0.011095 0.977291
+          0.000000 0.054001 0.000000
+          0.066667
+          -0.211428 0.013186 -0.010770 0.977245
+          -0.000000 0.054001 0.000000
+          0.100000
+          -0.211428 0.011477 -0.009305 0.977282
+          -0.000000 0.054001 0.000000
+          0.133333
+          -0.211427 0.009768 -0.007841 0.977314
+          0.000000 0.054001 0.000000
+          0.166667
+          -0.213380 0.007571 -0.008818 0.976900
+          -0.000000 0.054001 0.000000
+          0.200000
+          -0.214845 0.006594 -0.007842 0.976595
+          -0.000000 0.054001 0.000000
+          0.233333
+          -0.213868 0.005861 -0.006865 0.976821
+          0.000000 0.054001 0.000000
+          0.266667
+          -0.213057 0.002973 -0.008982 0.976994
+          0.000000 0.054001 0.000001
+          0.300000
+          -0.212243 0.000084 -0.011098 0.977154
+          -0.000000 0.054001 0.000000
+          0.333333
+          -0.211426 -0.002804 -0.013215 0.977301
+          0.000000 0.054000 0.000000
+          0.366667
+          -0.208254 -0.005307 -0.012727 0.977978
+          -0.000000 0.054001 0.000000
+          0.400000
+          -0.205078 -0.007809 -0.012239 0.978638
+          -0.000000 0.054001 0.000000
+          0.433333
+          -0.204590 -0.009274 -0.012240 0.978727
+          0.000000 0.054001 0.000000
+          0.466667
+          -0.206055 -0.008541 -0.011751 0.978433
+          -0.000000 0.054001 0.000000
+          0.500000
+          -0.207519 -0.007809 -0.011263 0.978135
+          0.000000 0.054001 0.000000
+          0.533333
+          -0.211183 -0.006223 -0.011507 0.977359
+          0.000000 0.054000 0.000000
+          0.566667
+          -0.214844 -0.004636 -0.011750 0.976567
+          0.000000 0.054000 0.000000
+          0.600000
+          -0.217286 -0.002897 -0.012482 0.976024
+          -0.000000 0.054001 0.000000
+          0.633333
+          -0.219727 -0.001157 -0.013214 0.975471
+          -0.000000 0.054001 0.000000
+          0.666667
+          -0.222657 0.000826 -0.012726 0.974814
+          -0.000000 0.054001 0.000000
+          0.700000
+          -0.223145 0.002932 -0.012237 0.974704
+          0.000000 0.054000 0.000001
+          0.733334
+          -0.221193 0.005129 -0.012481 0.975137
+          -0.000000 0.054000 0.000000
+          0.766667
+          -0.219239 0.007327 -0.012724 0.975561
+          0.000000 0.054000 0.000000
+          0.800000
+          -0.218264 0.010013 -0.013700 0.975742
+          0.000000 0.054001 0.000000
+          0.833334
+          -0.217287 0.012698 -0.014676 0.975915
+          0.000000 0.054001 0.000000
+          0.866667
+          -0.214359 0.014652 -0.013211 0.976556
+          0.000000 0.054000 0.000001
+          0.900000
+          -0.211428 0.016604 -0.011746 0.977182
+          0.000000 0.054000 0.000000
+          0.933334
+          -0.211428 0.015872 -0.011258 0.977200
+          -0.000000 0.054000 0.000000
+          0.966667
+          -0.211428 0.015139 -0.010769 0.977217
+          0.000000 0.054000 0.000000
+          1.000000
+          -0.211428 0.013186 -0.010770 0.977245
+          -0.000000 0.054001 0.000000
+          1.033334
+          -0.211428 0.011477 -0.009305 0.977282
+          0.000000 0.054001 0.000000
+          1.066667
+          -0.211427 0.009768 -0.007841 0.977314
+          -0.000000 0.054001 0.000000
+          1.100000
+          -0.213380 0.007571 -0.008818 0.976900
+          -0.000000 0.054000 0.000000
+          1.133334
+          -0.214845 0.006594 -0.007842 0.976594
+          0.000000 0.054000 0.000000
+          1.166667
+          -0.213868 0.005861 -0.006865 0.976821
+          -0.000000 0.054001 0.000000
+          1.200000
+          -0.213057 0.002973 -0.008982 0.976994
+          -0.000000 0.054000 0.000000
+          1.233333
+          -0.212243 0.000084 -0.011098 0.977154
+          -0.000000 0.054000 0.000000
+          1.266667
+          -0.211426 -0.002804 -0.013215 0.977301
+          -0.000000 0.054000 0.000000
+          1.300000
+          -0.208254 -0.005307 -0.012727 0.977978
+          -0.000000 0.054000 0.000000
+          1.333333
+          -0.205078 -0.007809 -0.012239 0.978638
+          -0.000000 0.054001 0.000000
+          1.366667
+          -0.204590 -0.009274 -0.012240 0.978727
+          -0.000000 0.054000 0.000000
+          1.400000
+          -0.206055 -0.008541 -0.011751 0.978433
+          -0.000000 0.054000 0.000000
+          1.433333
+          -0.207519 -0.007809 -0.011263 0.978135
+          -0.000000 0.054001 0.000000
+          1.466667
+          -0.211183 -0.006223 -0.011507 0.977359
+          0.000000 0.054000 0.000000
+          1.500000
+          -0.214844 -0.004636 -0.011750 0.976567
+          -0.000000 0.054001 0.000000
+          1.533333
+          -0.217286 -0.002897 -0.012482 0.976024
+          0.000000 0.054001 0.000001
+          1.566666
+          -0.219727 -0.001157 -0.013214 0.975471
+          0.000000 0.054001 0.000000
+          1.600000
+          -0.222657 0.000826 -0.012726 0.974814
+          -0.000000 0.054001 0.000000
+          1.633333
+          -0.223145 0.002932 -0.012237 0.974704
+          0.000000 0.054000 0.000001
+          1.666666
+          -0.221193 0.005129 -0.012481 0.975137
+          0.000000 0.054001 0.000000
+          1.700000
+          -0.219240 0.007327 -0.012724 0.975561
+          0.000000 0.054000 0.000000
+          1.733333
+          -0.218264 0.010013 -0.013700 0.975742
+          -0.000000 0.054000 0.000001
+          1.766666
+          -0.217287 0.012698 -0.014676 0.975915
+          0.000000 0.054000 0.000000
+          1.800000
+          -0.214359 0.014652 -0.013211 0.976556
+          -0.000000 0.054000 0.000000
+          1.833333
+          -0.211428 0.016604 -0.011746 0.977182
+          0.000000 0.054000 0.000001
+          1.866666
+          -0.211428 0.015872 -0.011258 0.977200
+          0.000000 0.054001 0.000000
+          1.899999
+          -0.211428 0.015139 -0.010769 0.977217
+          0.000000 0.054001 0.000001
+          1.933333
+          -0.211428 0.013186 -0.010770 0.977245
+          0.000000 0.054001 0.000000
+          1.966666
+          -0.211428 0.011477 -0.009305 0.977282
+          0.000000 0.054001 0.000000
+          1.999999
+          -0.211427 0.009768 -0.007841 0.977314
+          0.000000 0.054001 0.000000
+          2.033333
+          -0.213380 0.007571 -0.008818 0.976900
+          -0.000000 0.054000 0.000000
+          2.066666
+          -0.214845 0.006594 -0.007842 0.976594
+          0.000000 0.054000 0.000000
+          2.099999
+          -0.213868 0.005861 -0.006865 0.976821
+          -0.000000 0.054001 0.000000
+          2.133333
+          -0.213057 0.002973 -0.008982 0.976994
+          -0.000000 0.054000 0.000000
+          2.166666
+          -0.212243 0.000084 -0.011098 0.977154
+          0.000000 0.054000 0.000000
+          2.199999
+          -0.211426 -0.002804 -0.013215 0.977301
+          0.000000 0.054000 0.000000
+          2.233333
+          -0.208254 -0.005307 -0.012727 0.977978
+          0.000000 0.054001 0.000000
+          2.266666
+          -0.205078 -0.007809 -0.012239 0.978638
+          0.000000 0.054001 0.000000
+          2.299999
+          -0.204590 -0.009274 -0.012240 0.978727
+          0.000000 0.054001 0.000000
+          2.333333
+          -0.206055 -0.008541 -0.011751 0.978433
+          -0.000000 0.054001 0.000000
+          2.366666
+          -0.207519 -0.007809 -0.011263 0.978135
+          0.000000 0.054000 0.000000
+          2.399999
+          -0.211183 -0.006223 -0.011507 0.977359
+          0.000000 0.054000 0.000000
+          2.433332
+          -0.214844 -0.004636 -0.011750 0.976567
+          -0.000000 0.054000 0.000000
+          2.466666
+          -0.217286 -0.002927 -0.012482 0.976024
+          0.000000 0.054001 0.000000
+          2.499999
+          -0.219727 -0.001218 -0.013214 0.975471
+          0.000000 0.054001 0.000001
+          2.533332
+          -0.222657 0.000826 -0.012726 0.974813
+          0.000000 0.054000 0.000000
+          2.566666
+          -0.223145 0.002932 -0.012237 0.974704
+          -0.000000 0.054000 0.000001
+          2.599999
+          -0.221193 0.005129 -0.012481 0.975137
+          -0.000000 0.054001 0.000000
+          2.633332
+          -0.219240 0.007327 -0.012724 0.975561
+          -0.000000 0.054000 0.000000
+          2.666666
+          -0.218264 0.010012 -0.013700 0.975742
+          -0.000000 0.054000 0.000000
+          2.699999
+          -0.217287 0.012698 -0.014676 0.975915
+          0.000000 0.054001 0.000000
+          2.733332
+          -0.213871 0.015140 -0.013211 0.976655
+          0.000000 0.054000 0.000000
+          2.766665
+          -0.210451 0.017581 -0.011746 0.977376
+          0.000000 0.054001 0.000000
+          2.799999
+      "C_Neck_sjnt_2"
+      85
+          -0.018559 0.042971 -0.020499 0.998694
+          0.000000 0.054001 0.000000
+          0.000000
+          -0.018722 0.041506 -0.020499 0.998752
+          -0.000000 0.054001 0.000000
+          0.033333
+          -0.018884 0.040041 -0.020500 0.998809
+          0.000000 0.054001 0.000000
+          0.066667
+          -0.019047 0.038576 -0.020500 0.998864
+          0.000000 0.054001 -0.000000
+          0.100000
+          -0.018314 0.036623 -0.019524 0.998971
+          -0.000000 0.054001 0.000000
+          0.133333
+          -0.017582 0.034670 -0.018548 0.999072
+          0.000000 0.054001 0.000000
+          0.166667
+          -0.019046 0.032717 -0.020501 0.999073
+          -0.000000 0.054001 0.000000
+          0.200000
+          -0.020023 0.031252 -0.019525 0.999120
+          -0.000000 0.054001 0.000000
+          0.233333
+          -0.019046 0.030519 -0.018060 0.999189
+          -0.000000 0.054001 -0.000000
+          0.266667
+          -0.018720 0.027997 -0.020177 0.999229
+          -0.000000 0.054001 -0.000000
+          0.300000
+          -0.018394 0.025475 -0.022293 0.999258
+          -0.000000 0.054001 0.000000
+          0.333333
+          -0.018069 0.022952 -0.024410 0.999275
+          -0.000000 0.054001 -0.000000
+          0.366667
+          -0.016726 0.020267 -0.024410 0.999357
+          -0.000000 0.054001 0.000000
+          0.400000
+          -0.015382 0.017582 -0.024411 0.999429
+          -0.000000 0.054001 -0.000000
+          0.433333
+          -0.015993 0.016971 -0.025143 0.999412
+          0.000000 0.054001 0.000000
+          0.466667
+          -0.016603 0.016361 -0.025876 0.999393
+          0.000000 0.054001 0.000000
+          0.500000
+          -0.018556 0.017826 -0.025062 0.999355
+          0.000000 0.054001 0.000000
+          0.533333
+          -0.020510 0.019290 -0.024248 0.999309
+          -0.000000 0.054001 -0.000000
+          0.566667
+          -0.022463 0.020754 -0.023434 0.999258
+          0.000000 0.054001 0.000000
+          0.600000
+          -0.023440 0.022626 -0.023108 0.999202
+          0.000000 0.054001 0.000000
+          0.633333
+          -0.024417 0.024498 -0.022782 0.999142
+          -0.000000 0.054001 -0.000000
+          0.666667
+          -0.025393 0.026369 -0.022456 0.999077
+          0.000000 0.054001 -0.000000
+          0.700000
+          -0.025393 0.028078 -0.021479 0.999052
+          0.000000 0.054001 -0.000000
+          0.733334
+          -0.023196 0.030397 -0.021234 0.999043
+          -0.000000 0.054001 -0.000000
+          0.766667
+          -0.020999 0.032717 -0.020990 0.999024
+          -0.000000 0.054001 0.000000
+          0.800000
+          -0.021244 0.035647 -0.021722 0.998902
+          0.000000 0.054001 0.000000
+          0.833334
+          -0.021488 0.038576 -0.022453 0.998772
+          0.000000 0.054001 0.000000
+          0.866667
+          -0.019780 0.040530 -0.020988 0.998762
+          0.000000 0.054001 0.000000
+          0.900000
+          -0.018071 0.042483 -0.019523 0.998743
+          0.000000 0.054001 -0.000000
+          0.933334
+          -0.018559 0.041750 -0.019767 0.998760
+          0.000000 0.054001 0.000000
+          0.966667
+          -0.019047 0.041018 -0.020011 0.998776
+          0.000000 0.054001 0.000000
+          1.000000
+          -0.019047 0.038576 -0.020500 0.998864
+          0.000000 0.054001 -0.000000
+          1.033334
+          -0.018314 0.036623 -0.019524 0.998971
+          0.000000 0.054001 0.000000
+          1.066667
+          -0.017582 0.034670 -0.018548 0.999072
+          -0.000000 0.054001 -0.000000
+          1.100000
+          -0.019046 0.032717 -0.020501 0.999073
+          -0.000000 0.054001 0.000000
+          1.133334
+          -0.020023 0.031252 -0.019525 0.999120
+          0.000000 0.054001 0.000000
+          1.166667
+          -0.019046 0.030519 -0.018060 0.999189
+          -0.000000 0.054001 -0.000000
+          1.200000
+          -0.018720 0.027997 -0.020177 0.999229
+          0.000000 0.054001 -0.000000
+          1.233333
+          -0.018394 0.025475 -0.022293 0.999258
+          0.000000 0.054001 -0.000000
+          1.266667
+          -0.018069 0.022952 -0.024410 0.999275
+          0.000000 0.054001 0.000000
+          1.300000
+          -0.016726 0.020267 -0.024410 0.999357
+          0.000000 0.054001 0.000000
+          1.333333
+          -0.015382 0.017582 -0.024411 0.999429
+          0.000000 0.054001 -0.000000
+          1.366667
+          -0.015993 0.016971 -0.025143 0.999412
+          0.000000 0.054001 0.000000
+          1.400000
+          -0.016603 0.016361 -0.025876 0.999393
+          0.000000 0.054001 -0.000000
+          1.433333
+          -0.018556 0.017826 -0.025062 0.999355
+          -0.000000 0.054001 0.000000
+          1.466667
+          -0.020510 0.019290 -0.024248 0.999309
+          -0.000000 0.054001 0.000000
+          1.500000
+          -0.022463 0.020754 -0.023434 0.999258
+          0.000000 0.054001 0.000000
+          1.533333
+          -0.023440 0.022626 -0.023108 0.999202
+          0.000000 0.054001 0.000000
+          1.566666
+          -0.024417 0.024498 -0.022782 0.999142
+          0.000000 0.054001 -0.000000
+          1.600000
+          -0.025393 0.026369 -0.022456 0.999077
+          0.000000 0.054001 0.000000
+          1.633333
+          -0.025393 0.028078 -0.021479 0.999052
+          0.000000 0.054001 -0.000000
+          1.666666
+          -0.023196 0.030397 -0.021234 0.999043
+          0.000000 0.054001 0.000000
+          1.700000
+          -0.020999 0.032717 -0.020990 0.999024
+          0.000000 0.054001 0.000000
+          1.733333
+          -0.021244 0.035647 -0.021722 0.998902
+          0.000000 0.054001 0.000000
+          1.766666
+          -0.021488 0.038576 -0.022453 0.998772
+          0.000000 0.054001 -0.000000
+          1.800000
+          -0.019780 0.040530 -0.020988 0.998762
+          -0.000000 0.054001 -0.000000
+          1.833333
+          -0.018071 0.042482 -0.019523 0.998743
+          0.000000 0.054001 0.000000
+          1.866666
+          -0.018559 0.041750 -0.019767 0.998760
+          0.000000 0.054001 -0.000000
+          1.899999
+          -0.019047 0.041018 -0.020011 0.998776
+          0.000000 0.054001 0.000000
+          1.933333
+          -0.019047 0.038576 -0.020500 0.998864
+          0.000000 0.054001 0.000000
+          1.966666
+          -0.018314 0.036623 -0.019524 0.998971
+          -0.000000 0.054001 -0.000000
+          1.999999
+          -0.017582 0.034670 -0.018548 0.999072
+          -0.000000 0.054001 -0.000000
+          2.033333
+          -0.019046 0.032717 -0.020501 0.999073
+          0.000000 0.054001 0.000000
+          2.066666
+          -0.020023 0.031252 -0.019525 0.999120
+          0.000000 0.054001 0.000000
+          2.099999
+          -0.019046 0.030519 -0.018060 0.999190
+          0.000000 0.054001 0.000000
+          2.133333
+          -0.018721 0.027997 -0.020177 0.999229
+          0.000000 0.054001 0.000000
+          2.166666
+          -0.018395 0.025475 -0.022293 0.999258
+          0.000000 0.054001 -0.000000
+          2.199999
+          -0.018069 0.022952 -0.024410 0.999275
+          -0.000000 0.054001 0.000000
+          2.233333
+          -0.016725 0.020267 -0.024410 0.999357
+          0.000000 0.054001 -0.000000
+          2.266666
+          -0.015382 0.017582 -0.024411 0.999429
+          0.000000 0.054001 -0.000000
+          2.299999
+          -0.015992 0.016971 -0.025143 0.999412
+          0.000000 0.054001 0.000000
+          2.333333
+          -0.016603 0.016361 -0.025876 0.999393
+          0.000000 0.054001 0.000000
+          2.366666
+          -0.018556 0.017825 -0.025062 0.999355
+          0.000000 0.054001 0.000001
+          2.399999
+          -0.020509 0.019290 -0.024248 0.999309
+          0.000000 0.054001 0.000000
+          2.433332
+          -0.022463 0.020754 -0.023434 0.999258
+          0.000000 0.054001 -0.000000
+          2.466666
+          -0.023196 0.022585 -0.022945 0.999212
+          0.000000 0.054001 0.000000
+          2.499999
+          -0.023928 0.024416 -0.022457 0.999163
+          0.000000 0.054001 0.000000
+          2.533332
+          -0.024661 0.026247 -0.021968 0.999110
+          0.000000 0.054001 0.000000
+          2.566666
+          -0.025393 0.028078 -0.021479 0.999052
+          0.000000 0.054001 0.000000
+          2.599999
+          -0.023197 0.030397 -0.021234 0.999043
+          -0.000000 0.054001 0.000000
+          2.633332
+          -0.021000 0.032717 -0.020990 0.999023
+          0.000000 0.054001 0.000000
+          2.666666
+          -0.021000 0.035647 -0.021477 0.998913
+          0.000000 0.054001 0.000000
+          2.699999
+          -0.021000 0.038576 -0.021965 0.998794
+          -0.000000 0.054001 -0.000000
+          2.733332
+          -0.019780 0.040774 -0.021232 0.998747
+          0.000000 0.054001 -0.000001
+          2.766665
+          -0.018559 0.042971 -0.020499 0.998694
+          0.000000 0.054001 0.000000
+          2.799999
+      "C_Head_sjnt_0"
+      85
+          -0.103524 0.074208 -0.008288 0.991820
+          0.000000 0.054000 0.000000
+          0.000000
+          -0.095712 0.073233 -0.010729 0.992654
+          0.000000 0.054000 0.000000
+          0.033333
+          -0.089852 0.072256 -0.006579 0.993309
+          0.000000 0.054000 -0.000000
+          0.066667
+          -0.090829 0.071278 0.000211 0.993312
+          -0.000000 0.054000 -0.000000
+          0.100000
+          -0.100594 0.069324 -0.000400 0.992509
+          -0.000000 0.054000 -0.000000
+          0.133333
+          -0.107430 0.068346 0.003308 0.991855
+          -0.000000 0.054000 0.000000
+          0.166667
+          -0.107430 0.066391 0.010265 0.991940
+          -0.000000 0.054000 0.000000
+          0.200000
+          -0.105477 0.064439 0.007579 0.992303
+          -0.000000 0.054000 -0.000000
+          0.233333
+          -0.107429 0.063464 -0.000264 0.992185
+          -0.000000 0.054000 0.000000
+          0.266667
+          -0.106453 0.061998 0.002818 0.992379
+          0.000000 0.054000 0.000000
+          0.300000
+          -0.102547 0.060045 0.007334 0.992887
+          -0.000000 0.054000 -0.000000
+          0.333333
+          -0.100106 0.056383 0.008799 0.993339
+          0.000000 0.054000 0.000000
+          0.366667
+          -0.097663 0.052721 0.010263 0.993769
+          0.000000 0.054000 0.000000
+          0.400000
+          -0.099616 0.050767 0.010262 0.993677
+          -0.000000 0.054000 -0.000000
+          0.433333
+          -0.094247 0.051012 0.013925 0.994144
+          0.000000 0.054000 0.000000
+          0.466667
+          -0.088874 0.051255 0.017587 0.994568
+          0.000000 0.054000 0.000000
+          0.500000
+          -0.090827 0.052721 0.013681 0.994376
+          0.000000 0.054000 0.000000
+          0.533333
+          -0.096687 0.054674 0.010263 0.993759
+          0.000000 0.054000 -0.000000
+          0.566667
+          -0.102546 0.056138 0.010751 0.993085
+          -0.000000 0.054000 0.000000
+          0.600000
+          -0.112312 0.057601 0.011728 0.991933
+          -0.000000 0.054000 -0.000000
+          0.633333
+          -0.116218 0.059554 0.011240 0.991373
+          0.000000 0.054000 0.000000
+          0.666667
+          -0.112314 0.061508 0.012950 0.991683
+          0.000000 0.054000 0.000000
+          0.700000
+          -0.108406 0.063461 0.014659 0.991971
+          -0.000000 0.054000 -0.000000
+          0.733334
+          -0.111336 0.066390 0.015636 0.991439
+          -0.000000 0.054000 -0.000000
+          0.766667
+          -0.109384 0.069319 0.018567 0.991406
+          -0.000000 0.054000 -0.000000
+          0.800000
+          -0.103525 0.072249 0.019544 0.991807
+          0.000000 0.054000 0.000000
+          0.833334
+          -0.098642 0.075180 0.015638 0.992156
+          -0.000000 0.054000 -0.000000
+          0.866667
+          -0.100113 0.075675 0.004530 0.992084
+          0.000000 0.054000 -0.000000
+          0.900000
+          -0.101571 0.076161 -0.006578 0.991887
+          -0.000000 0.054000 0.000000
+          0.933334
+          -0.095712 0.074210 -0.010241 0.992586
+          0.000000 0.054000 0.000000
+          0.966667
+          -0.090829 0.072256 -0.006579 0.993220
+          0.000000 0.054000 0.000000
+          1.000000
+          -0.091805 0.071278 0.000057 0.993223
+          -0.000000 0.054000 0.000000
+          1.033334
+          -0.100594 0.069324 -0.000461 0.992509
+          0.000000 0.054000 0.000000
+          1.066667
+          -0.107430 0.067369 0.003308 0.991922
+          0.000000 0.054000 0.000000
+          1.100000
+          -0.107430 0.066391 0.010265 0.991940
+          0.000000 0.054000 -0.000000
+          1.133334
+          -0.105477 0.064439 0.007579 0.992303
+          0.000000 0.054000 0.000000
+          1.166667
+          -0.107429 0.063464 -0.000279 0.992185
+          0.000000 0.054000 0.000000
+          1.200000
+          -0.106453 0.061998 0.002818 0.992379
+          -0.000000 0.054000 -0.000000
+          1.233333
+          -0.102547 0.060045 0.007334 0.992887
+          -0.000000 0.054000 0.000000
+          1.266667
+          -0.100106 0.056383 0.008799 0.993339
+          0.000000 0.054000 0.000000
+          1.300000
+          -0.097663 0.052721 0.010263 0.993769
+          -0.000000 0.054000 -0.000000
+          1.333333
+          -0.099616 0.050767 0.010262 0.993677
+          -0.000000 0.054000 -0.000000
+          1.366667
+          -0.094247 0.051012 0.013925 0.994144
+          0.000000 0.054000 -0.000000
+          1.400000
+          -0.088874 0.051255 0.017587 0.994568
+          0.000000 0.054000 0.000000
+          1.433333
+          -0.090827 0.052721 0.013681 0.994376
+          -0.000000 0.054000 -0.000000
+          1.466667
+          -0.096687 0.054674 0.010263 0.993759
+          0.000000 0.054000 0.000000
+          1.500000
+          -0.102546 0.056138 0.010751 0.993085
+          -0.000000 0.054000 0.000000
+          1.533333
+          -0.112312 0.057601 0.011728 0.991933
+          0.000000 0.054000 0.000000
+          1.566666
+          -0.116218 0.059554 0.011240 0.991373
+          -0.000000 0.054000 0.000000
+          1.600000
+          -0.112314 0.061508 0.012950 0.991683
+          0.000000 0.054000 0.000000
+          1.633333
+          -0.108406 0.063460 0.014659 0.991971
+          -0.000000 0.054000 -0.000000
+          1.666666
+          -0.111336 0.066390 0.015636 0.991439
+          0.000000 0.054000 0.000000
+          1.700000
+          -0.109384 0.069319 0.018567 0.991406
+          0.000000 0.054000 0.000000
+          1.733333
+          -0.103525 0.072249 0.019544 0.991807
+          0.000000 0.054000 0.000000
+          1.766666
+          -0.098642 0.075180 0.015639 0.992156
+          0.000000 0.054000 0.000000
+          1.800000
+          -0.100113 0.075675 0.004531 0.992084
+          0.000000 0.054000 -0.000000
+          1.833333
+          -0.101571 0.076161 -0.006578 0.991887
+          0.000000 0.054000 0.000000
+          1.866666
+          -0.095712 0.074210 -0.010241 0.992586
+          0.000000 0.054000 0.000000
+          1.899999
+          -0.090829 0.072256 -0.006579 0.993220
+          0.000000 0.054000 -0.000000
+          1.933333
+          -0.091805 0.071278 0.000057 0.993223
+          0.000000 0.054000 0.000000
+          1.966666
+          -0.100594 0.069324 -0.000461 0.992509
+          0.000000 0.054000 0.000000
+          1.999999
+          -0.107430 0.067369 0.003307 0.991922
+          -0.000000 0.054000 0.000000
+          2.033333
+          -0.107430 0.066391 0.010265 0.991940
+          -0.000000 0.054000 0.000000
+          2.066666
+          -0.105477 0.064439 0.007579 0.992303
+          -0.000000 0.054000 0.000000
+          2.099999
+          -0.107429 0.063464 -0.000279 0.992185
+          -0.000000 0.054000 -0.000000
+          2.133333
+          -0.106453 0.061998 0.002818 0.992379
+          0.000000 0.054000 0.000000
+          2.166666
+          -0.102547 0.060045 0.007334 0.992887
+          0.000000 0.054000 0.000000
+          2.199999
+          -0.100106 0.056383 0.008799 0.993339
+          0.000000 0.054000 0.000000
+          2.233333
+          -0.097663 0.052721 0.010263 0.993769
+          0.000000 0.054000 0.000000
+          2.266666
+          -0.099616 0.050767 0.010262 0.993677
+          -0.000000 0.054000 -0.000000
+          2.299999
+          -0.094247 0.051012 0.013925 0.994144
+          0.000000 0.054000 0.000000
+          2.333333
+          -0.088874 0.051255 0.017587 0.994568
+          0.000000 0.054000 0.000000
+          2.366666
+          -0.090827 0.052721 0.013681 0.994376
+          0.000000 0.054000 -0.000000
+          2.399999
+          -0.096686 0.054674 0.010263 0.993759
+          0.000000 0.053999 0.000000
+          2.433332
+          -0.102546 0.056138 0.010751 0.993085
+          0.000000 0.054000 0.000000
+          2.466666
+          -0.112312 0.057601 0.011728 0.991933
+          0.000000 0.054000 0.000000
+          2.499999
+          -0.116218 0.059554 0.011240 0.991373
+          0.000000 0.054000 0.000000
+          2.533332
+          -0.112802 0.061508 0.012706 0.991630
+          0.000000 0.053999 0.000000
+          2.566666
+          -0.109383 0.063460 0.014171 0.991871
+          0.000000 0.054000 0.000000
+          2.599999
+          -0.111336 0.066390 0.015636 0.991439
+          0.000000 0.054000 0.000000
+          2.633332
+          -0.109384 0.069319 0.019543 0.991387
+          0.000000 0.054000 0.000000
+          2.666666
+          -0.103525 0.072249 0.020520 0.991787
+          0.000000 0.054000 0.000000
+          2.699999
+          -0.098642 0.074204 0.015638 0.992229
+          -0.000000 0.054000 -0.000000
+          2.733332
+          -0.101091 0.074211 0.003676 0.992099
+          0.000000 0.054000 0.000000
+          2.766665
+          -0.103524 0.074208 -0.008287 0.991820
+          -0.000000 0.054000 -0.000000
+          2.799999

+ 8 - 0
Assets/LICENSE

@@ -0,0 +1,8 @@
+The following assets were taken from Horizon Zero Dawn:
+
+terrain1.bof and terrain2.bof contain the 'Mothers Heart' scene
+convex_hulls.bin contains point clouds used as the source for convex hulls
+heightfield1.bin contains a single 'tile' of terrain data
+Human.tof and Human/*.tof contain the Aloy ragdoll setup and a couple of sample animations mapped onto the ragdoll
+
+Permission was granted by Guerrilla Games to release this under the same MIT license as the rest of the project.

BIN
Assets/convex_hulls.bin


BIN
Assets/heightfield1.bin


BIN
Assets/terrain1.bof


BIN
Assets/terrain2.bof


BIN
Assets/ui.tga


+ 4 - 0
Build/.gitignore

@@ -0,0 +1,4 @@
+/Linux*
+/VS20*
+/Doxygen
+/CoverageReport

+ 5 - 0
Build/Android/.gitignore

@@ -0,0 +1,5 @@
+.idea
+.gradle
+.cxx
+build
+local.properties

+ 48 - 0
Build/Android/UnitTests/build.gradle

@@ -0,0 +1,48 @@
+plugins {
+    id 'com.android.application'
+}
+
+android {
+    compileSdk 31
+
+    defaultConfig {
+        applicationId "com.joltphysics.unittests"
+        minSdk 24
+        targetSdk 31
+        versionCode 1
+        versionName "1.0"
+        ndk.abiFilters 'arm64-v8a', 'x86_64'
+
+        externalNativeBuild {
+            cmake {
+                cppFlags '-std=c++17 -Wall -Werror -DJPH_STAT_COLLECTOR -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER'
+                arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static'
+            }
+        }
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    externalNativeBuild {
+        cmake {
+            path file('src/main/cpp/CMakeLists.txt')
+            version '3.10.2'
+        }
+    }
+
+    buildFeatures {
+        viewBinding true
+    }
+}
+
+dependencies {
+}

+ 21 - 0
Build/Android/UnitTests/src/main/AndroidManifest.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.joltphysics.unittests">
+
+    <application
+        android:allowBackup="true"
+        android:label="Jolt Physics Unit Tests"
+        android:supportsRtl="false"
+        android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+        <activity
+            android:name="android.app.NativeActivity"
+            android:exported="true">
+            <meta-data android:name="android.app.lib_name" android:value="UnitTests"/>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

+ 24 - 0
Build/Android/UnitTests/src/main/cpp/CMakeLists.txt

@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.10.2)
+
+project("JoltPhysicsUnitTests")
+
+# Make sure we include the app glue sources
+set(APP_GLUE_DIR ${ANDROID_NDK}/sources/android/native_app_glue)
+include_directories(${APP_GLUE_DIR})
+
+if(${ANDROID_ABI} STREQUAL "x86_64")
+	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mlzcnt -mpopcnt")
+endif()
+
+# Set repository root
+set(PHYSICS_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../")
+
+# Make targets
+include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake)
+include(${PHYSICS_REPO_ROOT}/UnitTests/UnitTests.cmake)
+
+# Link shared native library
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
+add_library(UnitTests SHARED ${UNIT_TESTS_SRC_FILES} ${APP_GLUE_DIR}/android_native_app_glue.c)
+target_include_directories(UnitTests PUBLIC Jolt ${JOLT_PHYSICS_ROOT} ${UNIT_TESTS_ROOT})
+target_link_libraries(UnitTests Jolt android log)

+ 17 - 0
Build/Android/build.gradle

@@ -0,0 +1,17 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+    repositories {
+        google()
+        mavenCentral()
+    }
+    dependencies {
+        classpath "com.android.tools.build:gradle:7.0.0"
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 19 - 0
Build/Android/gradle.properties

@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true

BIN
Build/Android/gradle/wrapper/gradle-wrapper.jar


+ 5 - 0
Build/Android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME

+ 185 - 0
Build/Android/gradlew

@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"

+ 89 - 0
Build/Android/gradlew.bat

@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 9 - 0
Build/Android/settings.gradle

@@ -0,0 +1,9 @@
+dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+rootProject.name = "Jolt Physics"
+include ':UnitTests'

+ 96 - 0
Build/CMakeLists.txt

@@ -0,0 +1,96 @@
+cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
+
+project(JoltPhysics CXX)
+
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+	set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Distribution")
+elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+	set(CMAKE_CONFIGURATION_TYPES "Debug;Release;ReleaseASAN;ReleaseUBSAN;ReleaseCoverage;Distribution")
+endif()
+
+if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
+	# Fill in the path to the asan libraries
+	set(CLANG_LIB_PATH "\"$(VSInstallDir)\\VC\\Tools\\Llvm\\x64\\lib\\clang\\${CMAKE_CXX_COMPILER_VERSION}\\lib\\windows\"")
+	
+	# 64 bit architecture
+	set(CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE "x64")
+
+	# Set runtime library
+	set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+
+	# Set general compiler flags
+	set(CMAKE_CXX_FLAGS "/std:c++17 /Zc:__cplusplus /GR- /Gm- /Wall /WX /EHsc /nologo /diagnostics:classic /FC /arch:AVX2 /fp:except- /Zc:inline /Zi /DWIN32 /D_WINDOWS /DUNICODE /D_UNICODE")
+	
+	# Set compiler flags for various configurations
+	set(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /GS /Od /Ob0 /RTC1 /DJPH_STAT_COLLECTOR /DJPH_PROFILE_ENABLED /DJPH_DEBUG_RENDERER")
+	set(CMAKE_CXX_FLAGS_RELEASE "/DNDEBUG /GS- /GL /Gy /O2 /Oi /Ot /DJPH_PROFILE_ENABLED /DJPH_DEBUG_RENDERER")
+	set(CMAKE_CXX_FLAGS_DISTRIBUTION "/DNDEBUG /GS- /GL /Gy /O2 /Oi /Ot")
+	set(CMAKE_CXX_FLAGS_RELEASEASAN "/DNDEBUG /DJPH_PROFILE_ENABLED /DJPH_DISABLE_TEMP_ALLOCATOR /DJPH_DEBUG_RENDERER -fsanitize=address /Od")
+	set(CMAKE_CXX_FLAGS_RELEASEUBSAN "/DNDEBUG /DJPH_PROFILE_ENABLED /DJPH_DEBUG_RENDERER -fsanitize=undefined,implicit-conversion")
+	set(CMAKE_CXX_FLAGS_RELEASECOVERAGE "/DNDEBUG -fprofile-instr-generate -fcoverage-mapping")
+
+	# Set linker flags
+	set(CMAKE_EXE_LINKER_FLAGS "/machine:x64 /SUBSYSTEM:WINDOWS /ignore:4221 /DEBUG:FASTLINK")
+	if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /fp:fast") # Clang doesn't use fast math because it cannot be turned off inside a single compilation unit
+		set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /DJPH_FLOATING_POINT_EXCEPTIONS_ENABLED") # Clang turns Float2 into a vector sometimes causing floating point exceptions
+		set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /DJPH_FLOATING_POINT_EXCEPTIONS_ENABLED")
+		set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /LTCG:incremental /OPT:ICF /OPT:REF")
+		set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "/LTCG")
+	elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /showFilenames -mavx2 -mfma -mf16c -mlzcnt -mpopcnt")
+		set(CMAKE_EXE_LINKER_FLAGS_RELEASEASAN "/SUBSYSTEM:CONSOLE /LIBPATH:${CLANG_LIB_PATH} clang_rt.asan-x86_64.lib -wholearchive:clang_rt.asan-x86_64.lib clang_rt.asan_cxx-x86_64.lib -wholearchive:clang_rt.asan_cxx-x86_64.lib")
+		set(CMAKE_EXE_LINKER_FLAGS_RELEASEUBSAN "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}")
+		set(CMAKE_EXE_LINKER_FLAGS_RELEASECOVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LIBPATH:${CLANG_LIB_PATH}")
+	endif()
+elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
+	# Switch to clang compiler
+	set(CMAKE_CXX_COMPILER "clang++")
+
+	# Set general compiler flags (do not use -ffast-math since it cannot be turned off in a single compilation unit)
+	set(CMAKE_CXX_FLAGS "-std=c++17 -I. -Wall -Werror")
+
+	# Platform specific compiler flags
+	if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64")
+		# X64
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2 -mfma -mf16c -mlzcnt -mpopcnt")
+	elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
+		# ARM64
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+	endif()
+
+	# Set compiler flags for various configurations
+	set(CMAKE_CXX_FLAGS_DEBUG "-g -D_DEBUG -DJPH_STAT_COLLECTOR -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER") # Clang reorders variables so that div by zero occurs if we enable exception checking
+	set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER")
+	set(CMAKE_CXX_FLAGS_DISTRIBUTION "-DNDEBUG")
+	set(CMAKE_CXX_FLAGS_RELEASEASAN "-DNDEBUG -DJPH_PROFILE_ENABLED -DJPH_DISABLE_TEMP_ALLOCATOR -DJPH_DEBUG_RENDERER -fsanitize=address")
+	set(CMAKE_CXX_FLAGS_RELEASEUBSAN "-DNDEBUG -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER -fsanitize=undefined,implicit-conversion")
+	set(CMAKE_CXX_FLAGS_RELEASECOVERAGE "-DNDEBUG -fprofile-instr-generate -fcoverage-mapping")
+
+	# Set linker flags
+	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpthread")
+endif()
+
+# Set linker flags
+set(CMAKE_EXE_LINKER_FLAGS_DISTRIBUTION "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+
+# Set repository root
+set(PHYSICS_REPO_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../)
+
+# Make Jolt Library
+include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake)
+
+# Create UnitTests executable
+include(${PHYSICS_REPO_ROOT}/UnitTests/UnitTests.cmake)
+add_executable(UnitTests ${UNIT_TESTS_SRC_FILES})
+target_include_directories(UnitTests PUBLIC ${JOLT_PHYSICS_ROOT} ${UNIT_TESTS_ROOT})
+target_link_libraries (UnitTests LINK_PUBLIC Jolt)
+if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
+	target_link_options(UnitTests PUBLIC "/SUBSYSTEM:CONSOLE")
+endif()
+
+if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
+	# Windows only targets
+	include(${PHYSICS_REPO_ROOT}/TestFramework/TestFramework.cmake)
+	include(${PHYSICS_REPO_ROOT}/Samples/Samples.cmake)
+endif()

+ 33 - 0
Build/README.md

@@ -0,0 +1,33 @@
+# Building Jolt Physics
+
+## Windows 10+ (CL - Default compiler)
+
+- Download Visual Studio 2019+ (Community or other edition)
+- Download CMake 3.15+ (https://cmake.org/download/)
+- Run cmake_vs2019_cl.bat
+- Open the resulting project file VS2019_CL\JoltPhysics.sln
+- Compile and run either 'Samples' or 'UnitTests'
+
+## Windows 10+ (Clang compiler)
+
+- Download Visual Studio 2019+ (Community or other edition)
+- Make sure to install "C++ Clang Compiler for Windows 11.0.0+" and "C++ Clang-cl for v142+ build tools (x64/x86)" using the Visual Studio Installer
+- Download CMake 3.15+ (https://cmake.org/download/)
+- Run cmake_vs2019_clang.bat
+- Open the resulting project file VS2019_Clang\JoltPhysics.sln
+- Compile and run either 'Samples' or 'UnitTests'
+
+## Linux (Debian flavor, x64 or ARM64)
+
+- Install clang (apt-get install clang)
+- Install cmake (apt-get install cmake)
+- Run: ./cmake_vs2019_cl.bat
+- Go to the Linux_Debug folder
+- Run: make -j 8 && ./UnitTests
+
+## Android
+
+- Install Android Studio 2020.3.1+ (https://developer.android.com/studio/)
+- Open the 'Android' folder in Android Studio and wait until gradle finishes
+- Select 'Run' / 'Run...' and 'UnitTests'
+- If the screen turns green after a while the unit tests succeeded, when red they failed (see the android log for details)

+ 19 - 0
Build/cmake_linux_clang.sh

@@ -0,0 +1,19 @@
+#!/bin/sh
+
+BUILD_TYPE=$1
+if [ -z $1 ] 
+then
+	BUILD_TYPE=Debug
+else
+	BUILD_TYPE=$1
+fi
+
+BUILD_DIR=Linux_$BUILD_TYPE
+
+echo Usage: ./cmake_linux_clang.sh [Configuration]
+echo "Possible configurations: Debug (default), Release, Distribution, ReleaseUBSAN, ReleaseASAN, ReleaseCoverage"
+echo Generating Makefile for build type \"$BUILD_TYPE\" in folder \"$BUILD_DIR\"
+
+cmake -S . -B $BUILD_DIR -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$BUILD_TYPE
+
+echo Compile by running \"make -j 8 \&\& ./UnitTests\" in folder \"$BUILD_DIR\"

+ 3 - 0
Build/cmake_vs2019_cl.bat

@@ -0,0 +1,3 @@
+@echo off
+cmake -S . -B VS2019_CL -G "Visual Studio 16 2019" -A x64
+echo Open VS2019_CL\JoltPhysics.sln to build the project.

+ 10 - 0
Build/cmake_vs2019_clang.bat

@@ -0,0 +1,10 @@
+@echo off
+cmake -S . -B VS2019_Clang -G "Visual Studio 16 2019" -A x64 -T ClangCL
+echo:
+echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+echo Make sure to install:
+echo - C++ Clang Compiler for Windows 11.0.0+
+echo - C++ Clang-cl for v142+ build tools (x64/x86)
+echo Using the Visual Studio Installer
+echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+echo Open VS2019_Clang/JoltPhysics.sln to build the project.

+ 9 - 0
Build/unit_tests_coverage.bat

@@ -0,0 +1,9 @@
+@echo off
+set PATH=%PATH%;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\Llvm\x64\bin\;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\
+msbuild VS2019_Clang\JoltPhysics.sln /p:Configuration=ReleaseCoverage
+cd VS2019_Clang\ReleaseCoverage
+UnitTests.exe
+llvm-profdata merge -sparse default.profraw -o default.profdata
+cd ..\..
+llvm-cov show -format=html -output-dir=CoverageReport .\VS2019_Clang\ReleaseCoverage\UnitTests.exe -instr-profile=.\VS2019_Clang\ReleaseCoverage\default.profdata
+CoverageReport\index.html

+ 209 - 0
Docs/Architecture.md

@@ -0,0 +1,209 @@
+# Architecture of Jolt Physics
+
+## Design Considerations
+
+So why create yet another physics engine? First of all, this has been a personal learning project and secondly I wanted to address some issues that I had with existing physics engines:
+
+* In games we usually need to do many more things than to simulate the physics world and we need to do this across multiple threads. We therefore place a lot of emphasis on concurrently accessing the physics simulation data outside of the main physics simulation update:
+	* Sections of the world can be loaded / unloaded in the background. A batch of physics bodies can be prepared on a background thread without locking or affecting the physics simulation and then inserted into the world all at once with a minimal impact on performance.
+	* Collision queries can run in parallel with other operations like insertion / removal of bodies. The query code is guaranteed to see a body in a consistent state, but when a body is changed during a collision query there is no guarantee if the change is visible to the query or not. If a thread modifies the position of a body and then does a collision query, it will immediately see the updated state (this is often a problem when working with a read version and a write version of the world).
+	* It is also possible to run collision queries in parallel to the main physics simulation by doing the broad phase query before the simulation step. This way, long running processes (like navigation mesh generation) can be spread out across multiple frames while still running the physics simulation every frame.
+* One of the main sources of performance problems we found was waking up too many bodies while loading / unloading content. Therefore, bodies will not automatically wake up when created and neighboring bodies will not be woken up when bodies are removed. This can be triggered manually if desired.
+* The simulation runs deterministically, so you could replicate a simulation to a remote client by merely replicating the inputs to the simulation.
+
+Some more information:
+
+* The simulation by this physics engine tries to simulate behavior of rigid bodies in the real world but makes approximations in the simulation so should mainly be used for games or VR simulations.
+* The code runs on x64 CPUs using SSE4.2/AVX/AVX2 instructions or ARM64 CPUs. It scales up well to multiple cores, although there are edge cases that it doesn't deal well with (e.g. all bodies form a big pile).
+* It has been tested on Windows, Linux, Android, and a popular game console that we'll call Platform Blue (JPH_PLATFORM_BLUE).
+* It uses C++17, compiles under MSVC 2019 and Clang 10+ and relies only on the STL library.
+* It doesn't make use of compiler generated RTTI or exceptions.
+* If you want to run under Platform Blue you'll need to provide your own build environment and PlatformBlue.h file (see Core.h for further info).
+
+## The architecture
+
+### Bodies
+
+We use a pretty traditional physics engine setup. We have rigid bodies ([Body](@ref JPH::Body)) that have attached collision volumes ([Shape](@ref JPH::Shape)). Bodies can either be static (not simulating), dynamic (moved by forces) or kinematic (moved by velocities only). Each moving body has a [MotionProperties](@ref JPH::MotionProperties) object that contains information about the movement of the object. Static bodies do not have this to save space (but they can be configured to have it if a static body needs to become dynamic during its lifetime by setting [BodyCreationSettings::mAllowDynamicOrKinematic](@ref JPH::BodyCreationSettings::mAllowDynamicOrKinematic)).
+
+Bodies are inserted into the [PhysicsSystem](@ref JPH::PhysicsSystem) through the [BodyInterface](@ref JPH::BodyInterface). The body interface comes in two flavors: A locking and a non-locking variant. The locking variant uses a mutex array (a fixed size array of mutexes, bodies are associated with a mutex through hashing and multiple bodies use the same mutex, see [MutexArray](@ref JPH::MutexArray)) to prevent concurrent access to the same body. The non-locking variant doesn't use mutexes, so requires the user to be careful.
+
+In general, body ID's ([BodyID](@ref JPH::BodyID)) are used to refer to bodies. You can access a body through the following construct:
+
+	JPH::BodyLockInterface lock_interface = physics_system.GetBodyLockInterface(); // Or GetBodyLockInterfaceNoLock
+	JPH::BodyID body_id = ...; // Obtain ID to body
+	
+	// Scoped lock
+	{
+		JPH::BodyLockRead lock(lock_interface, body_id);
+		if (lock.Succeeded()) // body_id may no longer be valid
+		{
+			const JPH::Body &body = lock.Body();
+	
+			// Do something with body
+			...
+		}
+	}
+
+When another thread has removed the body between the time the body ID was obtained and the lock, the lock will fail. While the lock is taken, other threads cannot modify the body, so it is safe to work with it. Each body ID contains a sequence number, so body ID's will only be reused after many add/remove cycles.
+
+### Constraints
+
+Bodies can be connected to each other using constraints ([Constraint](@ref JPH::Constraint)).
+
+The following constraints are available:
+
+* [FixedConstraint](@ref JPH::FixedConstraintSettings) - Will attach a body to another without any degrees of freedom.
+* [DistanceConstraint](@ref JPH::DistanceConstraintSettings) - Will attach two bodies with a stick (removing 1 degree of freedom).
+* [PointConstraint](@ref JPH::PointConstraintSettings) - Will attach two bodies in a single point (removing 3 degrees of freedom)
+* [HingeConstraint](@ref JPH::HingeConstraintSettings) - Will attach two bodies through a hinge.
+* [ConeConstraint](@ref JPH::ConeConstraintSettings) - Attaches two bodies in a point and will limit the rotation within a cone.
+* [SliderConstraint](@ref JPH::SliderConstraintSettings) - Attaches two bodies and allows only movement in a single translation axis (also known as prismatic constraint).
+* [SwingTwistConstraint](@ref JPH::SwingTwistConstraintSettings) - Attaches two bodies using a point constraint and a swing-twist constraint which approximates the shoulder joint of a human.
+* [SixDOFConstraint](@ref JPH::SixDOFConstraintSettings) - The most configurable joint allows specifying per translation axis and rotation axis what the limits are.
+* [PathConstraint](@ref JPH::PathConstraintSettings) - This constraint allows attaching two bodies connected through a Hermite spline path.
+
+Most of the constraints support motors ([MotorSettings](@ref JPH::MotorSettings)) and allow the relative position/orientation of the bodies to be driven to a particular velocity or position.
+
+Adding and removing constraints can be done from multiple threads, but the constraints themselves do not have any protection against concurrent access. We assume that constraints are owned by some object (e.g. a Ragdoll) and that object ensures that it only modifies its own constraints and contains its own synchronization logic. Constraints can be freely modified except during the physics simulation step.
+
+Contact constraints (when bodies collide) are not handled through the ([Constraint](@ref JPH::Constraint)) class but through the [ContactConstraintManager](@ref JPH::ContactConstraintManager) which is considered an internal class.
+
+### Broad Phase
+
+When bodies are added to the PhysicsSystem, they are inserted in the broad phase ([BroadPhaseQuadTree](@ref JPH::BroadPhaseQuadTree)). This provides quick coarse collision detection based on the axis aligned bounding box (AABB) of a body.
+
+The broad phase is divided in layers, each layer has a AABB quad tree associated with it. When constructing the physics system a [ObjectLayerPairFilter](@ref JPH::ObjectLayerPairFilter) needs to be provided which determines which object layers collide with which other layer. If two layers don't collide, the objects inside those layers cannot collide. A standard setup would be to have a MOVING and a NON_MOVING layer, where NON_MOVING doesn't collide with NON_MOVING and all other permutations collide. This ensures that all static bodies are in one tree (which is infrequently updated) and all dynamic bodies are in another (which is updated every simulation step). It is possible to create more layers like a BULLET layer for high detail collision bodies that are attached to lower detail simulation bodies, the MOVING layer would not collide with the BULLET layer, but when performing e.g. weapon collision queries you can filter for only objects in the BULLET layer.
+
+Since we want to access bodies concurrently the broad phase has special behavior. When a body moves, all nodes in the AABB tree from root to the node where the body resides will be expanded using a lock-free approach. This way multiple threads can move bodies at the same time without requiring a lock on the broad phase. Nodes that have been expanded are marked and during the next physics step a new tight-fitting tree will be built in the background while the physics step is running. This new tree will replace the old tree before the end of the simulation step. This is possible since no bodies can be added/removed during the physics step.
+
+When doing a query against the broad phase ([BroadPhaseQuery](@ref JPH::BroadPhaseQuery)), you generally will get a body ID for intersecting objects. If a collision query takes a long time to process the resulting bodies (e.g. across multiple simulation steps), you can safely keep using the body ID's as specified in the [Bodies](#Bodies) section.
+
+### Narrow Phase
+
+A narrow phase query ([NarrowPhaseQuery](@ref JPH::NarrowPhaseQuery)) will first query the broad phase for intersecting bodies and will under the protection of a body lock construct a transformed shape ([TransformedShape](@ref JPH::TransformedShape)) object. This object contains the transform, a reference counted shape and a body ID. Since the shape will not be deleted until you destroy the TransformedShape object, it is a consistent snapshot of the collision information of the body. This ensures that the body is only locked for a short time frame and makes it possible to do the bulk of the collision detection work outside the protection of a lock.
+ 
+For very long running jobs (e.g. navigation mesh creation) it is possible to query all transformed shapes in an area and then do the processing work using a long running thread without requiring additional locks (see [NarrowPhaseQuery::CollectTransformedShapes](@ref JPH::NarrowPhaseQuery::CollectTransformedShapes)).
+
+The narrow phase queries are all handled through the [GJK](@ref JPH::GJKClosestPoint) and [EPA](@ref JPH::EPAPenetrationDepth) algorithms.
+
+### The Simulation Step
+
+The simulation step [PhysicsSystem::Update](@ref JPH::PhysicsSystem::Update) uses jobs ([JobSystem](@ref JPH::JobSystem)) to perform the needed work. This allows spreading the workload across multiple CPU's. We use a Sequential Impulse solver with warm starting as described in [Modeling and Solving Constraints - Erin Catto](https://box2d.org/files/ErinCatto_ModelingAndSolvingConstraints_GDC2009.pdf)
+
+Each physics step can be divided into multiple sub steps. There are collision and integration steps. For each collision step we run X integration steps, so if you run the simulation at 60 Hz with 2 collision steps and 3 integration steps we run:
+
+* Collision
+	* Integration (1/360s)
+	* Integration (1/360s)
+	* Integration (1/360s)
+* Collision
+	* Integration (1/360s)
+	* Integration (1/360s)
+	* Integration (1/360s)
+
+In general, the system is stable when running at 60 Hz with 1 collision and 1 integration step.
+
+The job graph looks like this:
+
+![Job Graph Physics Step](PhysicsSystemUpdate.svg)
+
+Note that each job indicates if it reads/writes positions/velocities and if it deactivates/activates bodies. We do not allow jobs to read/write the same data concurrently. The arrows indicate the order in which jobs are executed. Yellow blocks mean that there are multiple jobs of this type. Dotted arrows have special meaning and are explained below.
+
+#### Broad Phase Update Prepare 
+
+This job will refit the AABBs of the broad phase. It does this by building a new tree while keeping the old one available as described in the [Broad Phase](#Broad Phase) section.
+
+#### Broad Phase Update Finalize
+
+This job will simply swap the new tree with the old tree. The old tree will be discarded at the beginning of the next PhysicsSystem::Update call so that any broad phase query can continue to run.
+
+#### Step Listeners
+
+You can register one or more step listeners (See [PhysicsSystem::AddStepListener](@ref JPH::PhysicsSystem::AddStepListener)). This job will call [PhysicsStepListener::OnStep](@ref JPH::PhysicsStepListener::OnStep) for every listener. This can be used to do work that needs to be done at the beginning of each step, e.g. set velocities on ragdoll bodies.
+
+#### Apply Gravity
+
+A number of these jobs run in parallel. Each job takes a batch of active bodies and applies gravity and damping (updating linear and angular velocity).
+
+#### Determine Active Constraints
+
+This job will go through all non-contact constraints and determine which constraints are active based on if the bodies that the constraint connects to are active.
+
+#### Build Islands from Constraints
+
+This job will go through all non-contact constraints and assign the involved bodies and constraint to the same island. Since we allow concurrent insertion/removal of bodies we do not want to keep island data across multiple simulation steps, so we recreate the islands from scratch every simulation step. The operation is lock-free and O(N) where N is the number of constraints. 
+
+If a constraint connects an active and a non-active body, the non-active body is woken up.
+
+#### Find Collisions
+
+This job will do broad and narrow phase checks. Initially a number of jobs are started based on the amount of active bodies. The job will do the following:
+
+- Take a batch of active bodies and collide them against the broadphase. 
+- When a collision pair is found it is inserted in a lock free queue to be processed later.
+- If the queue is full, it will be processed immediately (more Find Collisions jobs are spawned if not all CPU cores are occupied yet as the queue starts to fill up).
+- If there are no more active bodies to process, the job will start to perform narrow phase collision detection and set up contact constraints if any collisions are found.
+- As soon as a narrow phase pair is processed it will recheck if there are new active bodies to be processed (active bodies can be generated by an active body colliding with an inactive body) and if so process them.
+- When there are no more active bodies to test and no more collision pairs to be processed the job terminates.
+
+Note that this job cannot start until apply gravity is done because the velocity needs to be known for elastic collisions to be calculated properly.
+
+The contact points between the two bodies will be determined by the [GJK](@ref JPH::GJKClosestPoint) and [EPA](@ref JPH::EPAPenetrationDepth) algorithms. For each contact point we will calculate the face that belongs to that contact point. The faces of both bodies are clipped against each other ([ManifoldBetweenTwoFaces](@ref JPH::ManifoldBetweenTwoFaces)) so that we have a polygon (or point / line) that represents the contact between the two bodies (contact manifold). 
+
+Multiple contact manifolds with similar normals are merged together (PhysicsSystem::ProcessBodyPair::ReductionCollideShapeCollector). After this the contact constraints are created in the [ContactConstraintManager](@ref JPH::ContactConstraintManager) and their Jacobians / effective masses calculated.
+
+Contacting bodies are also linked together to form islands. This is the same operation as described in the [Build Islands From Constraints](#Build Islands From Constraints) section.
+
+The narrow phase makes use of a lock free contact cache. We have 2 caches, one that is used for reading (which contains the contacts from the previous step) and one for writing new contact pairs. When a contact point is preserved from the last simulation step, it will be copied from the read cache to the write cache.
+
+#### Setup Velocity Constraints
+
+This job will go through all non-contact constraints and prepare them for execution. This involves calculating Jacobians and effective masses for each constraint part.
+
+#### Finalize Islands
+
+This job will finalize the building of the simulation islands. Each island contains bodies that interact with each other through a contact point or through a constraint. These islands will be simulated separately in different jobs later. The finalization of the islands is an O(N) operation where N is the amount of active bodies (see [IslandBuilder::Finalize](@ref JPH::IslandBuilder::Finalize)).
+
+#### Set Body Island Idx
+
+This job does some housekeeping work that can be executed concurrent to the solver:
+
+* It will assign the island ID to all bodies (which is mainly used for debugging purposes) 
+
+#### Solve Velocity Constraints
+
+A number of these jobs will run in parallel. Each job takes the next unprocessed island and will run the iterative constraint solver for that island. It will first apply the impulses applied from the previous simulation step (which are stored in the contact cache) to warm start the solver. It will then repeatedly iterate over all contact and non-contact constraints until either the applied impulses are too small or a max iteration count is reached ([PhysicsSettings::mNumVelocitySteps](@ref JPH::PhysicsSettings::mNumVelocitySteps)). The result will be that the new velocities are known for all active bodies. In the last integration step, the applied impulses are stored in the contact cache for the next step.
+
+#### Integrate & Clamp Velocities
+
+This job will integrate the velocity and update the position. It will clamp the velocity to the max velocity. 
+
+Depending on the motion quality ([EMotionQuality](@ref JPH::EMotionQuality)) of the body, it will schedule a body for continuous collision detection (CCD) if its movement is bigger than some treshold based on the [inner radius](@ref JPH::Shape::GetInnerRadius)) of the shape. Find CCD Contact jobs are created on the fly depending on how many CCD bodies there are.
+
+#### Find CCD Contacts
+
+A number of jobs wil run in parallel and pick up bodies that have been scheduled for CCD and will do a linear cast to detect the first collision. It always allows movement of the object by a fraction if its inner radius in order to prevent it from getting fully stuck.
+
+#### Resolve CCD Contacts
+
+This job will take the collision results from the previous job and update position and velocity of the involved bodies. If an object hits another object, its time will be 'stolen' (it will move less far than it should according to its velocity).
+
+#### Finalize Contact Cache, Contact Removed Callbacks
+
+This job will:
+
+* Swap the read/write contact cache and prepare the contact cache for the next step. 
+* It will detect all contacts that existed previous step and do not exist anymore to fire callbacks for them through the [ContactListener](@ref JPH::ContactListener) interface.
+
+#### Solve Position Constraints, Update Bodies Broad Phase
+
+A number of these jobs will run in parallel. Each job takes the next unprocessed island and run the position based constraint solver. This fixes numerical drift that may have caused constrained bodies to separate (remember that the constraints are solved in the velocity domain, so errors get introduced when doing a linear integration step). It will run until either the applied position corrections are too small or until the max amount of iterations is reached ([PhysicsSettings::mNumPositionSteps](@ref JPH::PhysicsSettings::mNumPositionSteps)).
+
+It will also notify the broad phase of the new body positions / AABBs. 
+
+When objects move too little the body will be put to sleep. This is detected by taking the biggest two axis of the local space bounding box of the shape together with the center of mass of the shape (all points in world space) and keep track of 3 bounding spheres for those points over time. If the bounding spheres become too big, the bounding spheres are reset and the timer restarted. When the timer reaches a certain time, the object has is considered non-moving and is put to sleep.  
+
+#### Apply Gravity, Setup/Solve Velocity Constraints
+
+In the second and later integration sub steps, we combine the Apply Gravity, Setup Velocity Constraints and the Solve Velocity Constraints jobs. There are multiple of these jobs and they each process simulation islands until there are no more.

BIN
Docs/Logo.png


BIN
Docs/LogoSmall.png


Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
Docs/PhysicsSystemUpdate.gliffy


Файловите разлики са ограничени, защото са твърде много
+ 2 - 0
Docs/PhysicsSystemUpdate.svg


BIN
Docs/SwingTwistConstraint.png


+ 2571 - 0
Doxyfile

@@ -0,0 +1,2571 @@
+# Doxyfile 1.8.16
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "Jolt Physics"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "A multi core friendly Game Physics Engine"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           = .\Docs\LogoSmall.png
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = .\Build\Doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION  = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER         = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE  = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is
+# Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL   = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# (including Cygwin) ands Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = .\Jolt \
+                         .\Docs\Architecture.md \
+                         .\Docs\PhysicsSystemUpdate.svg
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.idl \
+                         *.ddl \
+                         *.odl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.cs \
+                         *.d \
+                         *.php \
+                         *.php4 \
+                         *.php5 \
+                         *.phtml \
+                         *.inc \
+                         *.m \
+                         *.markdown \
+                         *.md \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.pyw \
+                         *.f90 \
+                         *.f95 \
+                         *.f03 \
+                         *.f08 \
+                         *.f \
+                         *.for \
+                         *.tcl \
+                         *.vhd \
+                         *.vhdl \
+                         *.ucf \
+                         *.qsf \
+                         *.ice
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             = .
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = .\Docs\Architecture.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+# If clang assisted parsing is enabled you can provide the clang parser with the
+# path to the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
+# were built. This is equivalent to specifying the "-p" option to a clang tool,
+# such as clang-check. These options will then be passed to the parser.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+
+CLANG_DATABASE_PATH    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via Javascript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have Javascript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = YES
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               = JoltPhysics.chm
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           = "C:\Program Files (x86)\HTML Help Workshop\hhc.exe"
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = YES
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         =
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD    = \makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = _WIN32 \
+                         JPH_PROFILE_ENABLED \
+                         JPH_STAT_COLLECTOR
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES

+ 224 - 0
Jolt/AABBTree/AABBTreeBuilder.cpp

@@ -0,0 +1,224 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <AABBTree/AABBTreeBuilder.h>
+
+namespace JPH {
+
+AABBTreeBuilder::Node::Node()
+{ 
+	mChild[0] = nullptr; 
+	mChild[1] = nullptr; 
+}
+
+AABBTreeBuilder::Node::~Node() 
+{ 
+	delete mChild[0]; 
+	delete mChild[1]; 
+}
+
+uint AABBTreeBuilder::Node::GetMinDepth() const
+{
+	if (HasChildren())
+	{
+		uint left = mChild[0]->GetMinDepth();
+		uint right = mChild[1]->GetMinDepth();
+		return min(left, right) + 1;
+	}
+	else
+		return 1;
+}
+
+uint AABBTreeBuilder::Node::GetMaxDepth() const
+{
+	if (HasChildren())
+	{
+		uint left = mChild[0]->GetMaxDepth();
+		uint right = mChild[1]->GetMaxDepth();
+		return max(left, right) + 1;
+	}
+	else
+		return 1;
+}
+
+uint AABBTreeBuilder::Node::GetNodeCount() const
+{
+	if (HasChildren())
+		return mChild[0]->GetNodeCount() + mChild[1]->GetNodeCount() + 1;
+	else
+		return 1;
+}
+
+uint AABBTreeBuilder::Node::GetLeafNodeCount() const
+{
+	if (HasChildren())
+		return mChild[0]->GetLeafNodeCount() + mChild[1]->GetLeafNodeCount();
+	else
+		return 1;
+}
+
+uint AABBTreeBuilder::Node::GetTriangleCountInTree() const
+{
+	if (HasChildren())
+		return mChild[0]->GetTriangleCountInTree() + mChild[1]->GetTriangleCountInTree();
+	else
+		return GetTriangleCount();
+}
+
+void AABBTreeBuilder::Node::GetTriangleCountPerNode(float &outAverage, uint &outMin, uint &outMax) const
+{
+	outMin = INT_MAX;
+	outMax = 0;
+	outAverage = 0;
+	uint avg_divisor = 0;
+	GetTriangleCountPerNodeInternal(outAverage, avg_divisor, outMin, outMax);
+	if (avg_divisor > 0)
+		outAverage /= avg_divisor;
+}
+
+float AABBTreeBuilder::Node::CalculateSAHCost(float inCostTraversal, float inCostLeaf) const
+{
+	float surface_area = mBounds.GetSurfaceArea();
+	return surface_area > 0.0f? CalculateSAHCostInternal(inCostTraversal / surface_area, inCostLeaf / surface_area) : 0.0f;
+}
+
+void AABBTreeBuilder::Node::GetNChildren(uint inN, vector<const Node *> &outChildren) const
+{
+	JPH_ASSERT(outChildren.empty());
+
+	// Check if there is anything to expand
+	if (!HasChildren())
+		return;
+
+	// Start with the children of this node
+	outChildren.push_back(mChild[0]);
+	outChildren.push_back(mChild[1]);
+
+	size_t next = 0;
+	bool all_triangles = true;
+	while (outChildren.size() < inN)
+	{
+		// If we have looped over all nodes, start over with the first node again
+		if (next >= outChildren.size())
+		{
+			// If there only triangle nodes left, we have to terminate
+			if (all_triangles)
+				return; 
+			next = 0;
+			all_triangles = true;
+		}
+
+		// Try to expand this node into its two children
+		const Node *to_expand = outChildren[next];
+		if (to_expand->HasChildren())
+		{
+			outChildren.erase(outChildren.begin() + next);
+			outChildren.push_back(to_expand->mChild[0]);
+			outChildren.push_back(to_expand->mChild[1]);
+			all_triangles = false;
+		}
+		else
+		{
+			++next;
+		}
+	}
+}
+
+float AABBTreeBuilder::Node::CalculateSAHCostInternal(float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const
+{
+	if (HasChildren())
+		return inCostTraversalDivSurfaceArea * mBounds.GetSurfaceArea() 
+			+ mChild[0]->CalculateSAHCostInternal(inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea) 
+			+ mChild[1]->CalculateSAHCostInternal(inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea);
+	else
+		return inCostLeafDivSurfaceArea * mBounds.GetSurfaceArea() * GetTriangleCount();
+}
+
+void AABBTreeBuilder::Node::GetTriangleCountPerNodeInternal(float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const
+{
+	if (HasChildren())
+	{
+		mChild[0]->GetTriangleCountPerNodeInternal(outAverage, outAverageDivisor, outMin, outMax);
+		mChild[1]->GetTriangleCountPerNodeInternal(outAverage, outAverageDivisor, outMin, outMax);
+	}
+	else
+	{
+		outAverage += GetTriangleCount();
+		outAverageDivisor++;
+		outMin = min(outMin, GetTriangleCount());
+		outMax = max(outMax, GetTriangleCount());
+	}
+}
+
+AABBTreeBuilder::AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf) : 
+	mTriangleSplitter(inSplitter),
+	mMaxTrianglesPerLeaf(inMaxTrianglesPerLeaf) 
+{ 
+}
+
+AABBTreeBuilder::Node *AABBTreeBuilder::Build(AABBTreeBuilderStats &outStats)
+{
+	TriangleSplitter::Range initial = mTriangleSplitter.GetInitialRange();
+	Node *root = BuildInternal(initial);
+
+	float avg_triangles_per_leaf;
+	uint min_triangles_per_leaf, max_triangles_per_leaf;
+	root->GetTriangleCountPerNode(avg_triangles_per_leaf, min_triangles_per_leaf, max_triangles_per_leaf);
+
+	mTriangleSplitter.GetStats(outStats.mSplitterStats);
+
+	outStats.mSAHCost = root->CalculateSAHCost(1.0f, 1.0f);
+	outStats.mMinDepth = root->GetMinDepth();
+	outStats.mMaxDepth = root->GetMaxDepth();
+	outStats.mNodeCount = root->GetNodeCount();
+	outStats.mLeafNodeCount = root->GetLeafNodeCount();
+	outStats.mMaxTrianglesPerLeaf = mMaxTrianglesPerLeaf;
+	outStats.mTreeMinTrianglesPerLeaf = min_triangles_per_leaf;
+	outStats.mTreeMaxTrianglesPerLeaf = max_triangles_per_leaf;
+	outStats.mTreeAvgTrianglesPerLeaf = avg_triangles_per_leaf;
+
+	return root;
+}
+
+AABBTreeBuilder::Node *AABBTreeBuilder::BuildInternal(TriangleSplitter::Range &inTriangles)
+{
+	// Check if there are too many triangles left
+	if (inTriangles.Count() > mMaxTrianglesPerLeaf)
+	{
+		// Split triangles in two batches
+		TriangleSplitter::Range left, right;
+		if (!mTriangleSplitter.Split(inTriangles, left, right))
+		{
+			JPH_IF_DEBUG(Trace("AABBTreeBuilder: Doing random split for %d triangles (max per node: %d)!", (int)inTriangles.Count(), mMaxTrianglesPerLeaf);)
+			int half = inTriangles.Count() / 2;
+			JPH_ASSERT(half > 0);
+			left = TriangleSplitter::Range(inTriangles.mBegin, inTriangles.mBegin + half);
+			right = TriangleSplitter::Range(inTriangles.mBegin + half, inTriangles.mEnd);
+		}
+
+		// Recursively build
+		Node *node = new Node();
+		node->mChild[0] = BuildInternal(left);
+		node->mChild[1] = BuildInternal(right);
+		node->mBounds = node->mChild[0]->mBounds;
+		node->mBounds.Encapsulate(node->mChild[1]->mBounds);
+		return node;
+	}
+
+	// Create leaf node
+	Node *node = new Node();
+	node->mTriangles.reserve(inTriangles.Count());
+	for (uint i = inTriangles.mBegin; i < inTriangles.mEnd; ++i)
+	{
+		const IndexedTriangle &t = mTriangleSplitter.GetTriangle(i);
+		const VertexList &v = mTriangleSplitter.GetVertices();
+		node->mTriangles.push_back(t);
+		node->mBounds.Encapsulate(v, t);
+	}
+
+	return node;
+}
+
+} // JPH

+ 106 - 0
Jolt/AABBTree/AABBTreeBuilder.h

@@ -0,0 +1,106 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <TriangleSplitter/TriangleSplitter.h>
+#include <Geometry/AABox.h>
+
+namespace JPH {
+
+struct AABBTreeBuilderStats
+{
+	///@name Splitter stats
+	TriangleSplitter::Stats	mSplitterStats;							///< Stats returned by the triangle splitter algorithm
+
+	///@name Tree structure
+	float					mSAHCost = 0.0f;						///< Surface Area Heuristic cost of this tree
+	int						mMinDepth = 0;							///< Minimal depth of tree (number of nodes)
+	int						mMaxDepth = 0;							///< Maximum depth of tree (number of nodes)
+	int						mNodeCount = 0;							///< Number of nodes in the tree
+	int						mLeafNodeCount = 0;						///< Number of leaf nodes (that contain triangles)
+
+	///@name Configured stats
+	int						mMaxTrianglesPerLeaf = 0;				///< Configured max triangles per leaf
+
+	///@name Actual stats
+	int						mTreeMinTrianglesPerLeaf = 0;			///< Minimal amount of triangles in a leaf
+	int						mTreeMaxTrianglesPerLeaf = 0;			///< Maximal amount of triangles in a leaf
+	float					mTreeAvgTrianglesPerLeaf = 0.0f;		///< Average amount of triangles in leaf nodes
+};
+
+/// Helper class to build an AABB tree
+class AABBTreeBuilder
+{
+public:
+	/// A node in the tree, contains the AABox for the tree and any child nodes or triangles
+	class Node
+	{
+	public:
+		/// Constructor
+							Node();
+							~Node();
+
+		/// Get number of triangles in this node
+		inline uint			GetTriangleCount() const				{ return uint(mTriangles.size()); }
+
+		/// Check if this node has any children
+		inline bool			HasChildren() const						{ return mChild[0] != nullptr || mChild[1] != nullptr; }
+
+		/// Min depth of tree
+		uint				GetMinDepth() const;
+
+		/// Max depth of tree
+		uint				GetMaxDepth() const;
+
+		/// Number of nodes in tree
+		uint				GetNodeCount() const;
+
+		/// Number of leaf nodes in tree
+		uint				GetLeafNodeCount() const;
+
+		/// Get triangle count in tree
+		uint				GetTriangleCountInTree() const;
+
+		/// Calculate min and max triangles per node
+		void				GetTriangleCountPerNode(float &outAverage, uint &outMin, uint &outMax) const;
+
+		/// Calculate the total cost of the tree using the surface area heuristic
+		float				CalculateSAHCost(float inCostTraversal, float inCostLeaf) const;
+
+		/// Recursively get children (breadth first) to get in total inN children (or less if there are no more)
+		void				GetNChildren(uint inN, vector<const Node *> &outChildren) const;
+
+		/// Bounding box
+		AABox				mBounds;
+
+		/// Triangles (if no child nodes)
+		IndexedTriangleList mTriangles;
+
+		/// Child nodes (if no triangles)
+		Node *				mChild[2];
+
+	private:
+		friend class AABBTreeBuilder;
+
+		/// Recursive helper function to calculate cost of the tree
+		float				CalculateSAHCostInternal(float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const;
+
+		/// Recursive helper function to calculate min and max triangles per node
+		void				GetTriangleCountPerNodeInternal(float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const;
+	};
+
+	/// Constructor
+							AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf = 16);
+
+	/// Recursively build tree, returns the root node of the tree
+	Node *					Build(AABBTreeBuilderStats &outStats);
+
+private:
+	Node *					BuildInternal(TriangleSplitter::Range &inTriangles);
+
+	TriangleSplitter &		mTriangleSplitter;
+	const uint				mMaxTrianglesPerLeaf;
+};
+
+} // JPH

+ 316 - 0
Jolt/AABBTree/AABBTreeToBuffer.h

@@ -0,0 +1,316 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <AABBTree/AABBTreeBuilder.h>
+#include <Core/ByteBuffer.h>
+#include <Geometry/IndexedTriangle.h>
+#include <deque>
+
+namespace JPH {
+
+/// How the tree should be converted
+enum class EAABBTreeToBufferConvertMode
+{
+	DepthFirst,							///< Arrange the nodes depth first, put the triangles right after the leaf nodes (so interleaving them with nodes)
+	DepthFirstTrianglesLast,			///< Arrange the nodes depth first and put all triangles blocks after the last node block
+	BreadthFirst,						///< Arrange the nodes breadth first, put the triangles right after the leaf nodes (so interleaving them with nodes)
+	BreadthFirstTrianglesLast,			///< Arrange the nodes breadth first and put all triangles blocks after the last node block
+};
+
+/// Convert mode to string
+inline string ConvertToString(EAABBTreeToBufferConvertMode inConvertMode)
+{
+	switch (inConvertMode)
+	{
+	case EAABBTreeToBufferConvertMode::DepthFirst:					return "DepthFirst";
+	case EAABBTreeToBufferConvertMode::DepthFirstTrianglesLast:		return "DepthFirstTrianglesLast";
+	case EAABBTreeToBufferConvertMode::BreadthFirst:				return "BreadthFirst";
+	case EAABBTreeToBufferConvertMode::BreadthFirstTrianglesLast:	return "BreadthFirstTrianglesLast";
+	}
+
+	JPH_ASSERT(false);
+	return "Invalid";
+}
+
+/// Struct that holds statistics about the AABB tree that was built
+struct AABBTreeToBufferStats
+{
+	uint			mTotalSize = 0;									///< Total size of the built tree in bytes
+	uint			mNodesSize = 0;									///< Total size of all nodes in the tree in bytes
+	uint			mTrianglesSize = 0;								///< Total size of all triangles in the tree in bytes
+	float			mBytesPerTriangle = 0.0f;						///< Average number of bytes per triangle (includes all tree overhead)
+	string			mTriangleCodecName;								///< Name of the codec that was used to build the tree
+	float			mVerticesPerTriangle = 0.0f;					///< How many vertices a triangle on average has
+};
+		
+/// Conversion algorithm that converts an AABB tree to an optimized binary buffer
+template <class TriangleCodec, class NodeCodec>
+class AABBTreeToBuffer
+{
+public:
+	/// Header for the tree
+	using NodeHeader = typename NodeCodec::Header;
+
+	/// Size in bytes of the header of the tree
+	static const int HeaderSize = NodeCodec::HeaderSize;
+
+	/// Maximum number of children per node in the tree
+	static const int NumChildrenPerNode = NodeCodec::NumChildrenPerNode;
+
+	/// Header for the triangles
+	using TriangleHeader = typename TriangleCodec::TriangleHeader;
+
+	/// Size in bytes of the header for the triangles
+	static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize;
+
+	/// Convert AABB tree. Returns false if failed.
+	bool							Convert(const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, AABBTreeToBufferStats &outStats, string &outError, EAABBTreeToBufferConvertMode inConvertMode = EAABBTreeToBufferConvertMode::DepthFirst)
+	{
+		const typename NodeCodec::EncodingContext node_ctx;
+		typename TriangleCodec::EncodingContext tri_ctx;
+
+		// Estimate the amount of memory required
+		uint tri_count = inRoot->GetTriangleCountInTree();
+		uint node_count = inRoot->GetNodeCount();
+		uint leaf_node_count = inRoot->GetLeafNodeCount();
+		uint nodes_size = node_ctx.GetPessimisticMemoryEstimate(node_count, leaf_node_count);
+		uint total_size = HeaderSize + TriangleHeaderSize + nodes_size + tri_ctx.GetPessimisticMemoryEstimate(tri_count);
+		mTree.reserve(total_size);
+
+		// Reset counters
+		mNodesSize = 0;
+
+		// Add headers
+		NodeHeader *header = HeaderSize > 0? mTree.Allocate<NodeHeader>() : nullptr;
+		TriangleHeader *triangle_header = TriangleHeaderSize > 0? mTree.Allocate<TriangleHeader>() : nullptr;
+
+		struct NodeData
+		{
+											NodeData()									: mNode(nullptr), mNodeStart((uint)-1), mTriangleStart((uint)-1), mNumChildren(0), mParentChildNodeStart(nullptr), mParentTrianglesStart(nullptr) { }
+
+			const AABBTreeBuilder::Node *	mNode;										// Node that this entry belongs to
+			Vec3							mNodeBoundsMin;								// Quantized node bounds
+			Vec3							mNodeBoundsMax;
+			uint							mNodeStart;									// Start of node in mTree
+			uint							mTriangleStart;								// Start of the triangle data in mTree
+			uint							mNumChildren;								// Number of children
+			uint							mChildNodeStart[NumChildrenPerNode];		// Start of the children of the node in mTree
+			uint							mChildTrianglesStart[NumChildrenPerNode];	// Start of the triangle data in mTree
+			uint *							mParentChildNodeStart;						// Where to store mNodeStart (to patch mChildNodeStart of my parent)
+			uint *							mParentTrianglesStart;						// Where to store mTriangleStart (to patch mChildTrianglesStart of my parent)
+		};
+		
+		deque<NodeData *> to_process;
+		deque<NodeData *> to_process_triangles;
+		vector<NodeData> node_list;
+
+		node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array
+		
+		NodeData root;
+		root.mNode = inRoot;
+		root.mNodeBoundsMin = inRoot->mBounds.mMin;
+		root.mNodeBoundsMax = inRoot->mBounds.mMax;
+		node_list.push_back(root);
+		to_process.push_back(&node_list.back());
+
+		// Child nodes out of loop so we don't constantly realloc it
+		vector<const AABBTreeBuilder::Node *> child_nodes;
+		child_nodes.reserve(NumChildrenPerNode);
+
+		for (;;)
+		{
+			while (!to_process.empty())
+			{
+				// Get the next node to process
+				NodeData *node_data = nullptr;
+				switch (inConvertMode)
+				{
+				case EAABBTreeToBufferConvertMode::DepthFirst:
+				case EAABBTreeToBufferConvertMode::DepthFirstTrianglesLast:
+					node_data = to_process.back();
+					to_process.pop_back();
+					break;
+
+				case EAABBTreeToBufferConvertMode::BreadthFirst:
+				case EAABBTreeToBufferConvertMode::BreadthFirstTrianglesLast:
+					node_data = to_process.front();
+					to_process.pop_front();
+					break;
+
+				default:
+					JPH_ASSERT(false);
+					break;
+				}
+
+				// Due to quantization box could have become bigger, not smaller
+				JPH_ASSERT(AABox(node_data->mNodeBoundsMin, node_data->mNodeBoundsMax).Contains(node_data->mNode->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
+
+				// Collect the first NumChildrenPerNode sub-nodes in the tree
+				child_nodes.clear(); // Won't free the memory
+				node_data->mNode->GetNChildren(NumChildrenPerNode, child_nodes);
+				node_data->mNumChildren = (uint)child_nodes.size();
+
+				// Fill in default child bounds
+				Vec3 child_bounds_min[NumChildrenPerNode], child_bounds_max[NumChildrenPerNode];
+				for (size_t i = 0; i < NumChildrenPerNode; ++i)
+					if (i < child_nodes.size())
+					{
+						child_bounds_min[i] = child_nodes[i]->mBounds.mMin;
+						child_bounds_max[i] = child_nodes[i]->mBounds.mMax;
+					}
+					else
+					{
+						child_bounds_min[i] = Vec3::sZero();
+						child_bounds_max[i] = Vec3::sZero();
+					}
+
+				// Start a new node
+				uint old_size = (uint)mTree.size();
+				node_data->mNodeStart = node_ctx.NodeAllocate(node_data->mNode, node_data->mNodeBoundsMin, node_data->mNodeBoundsMax, child_nodes, child_bounds_min, child_bounds_max, mTree, outError);
+				if (node_data->mNodeStart == uint(-1))
+					return false;
+				mNodesSize += (uint)mTree.size() - old_size;
+
+				if (node_data->mNode->HasChildren())
+				{
+					for (size_t i = 0; i < child_nodes.size(); ++i)
+					{
+						// Depth first: Insert in reverse order so we process left child first when taking nodes from the back
+						size_t idx = (inConvertMode == EAABBTreeToBufferConvertMode::DepthFirst || inConvertMode == EAABBTreeToBufferConvertMode::DepthFirstTrianglesLast)? 
+							child_nodes.size() - 1 - i : i;
+					
+						// Due to quantization box could have become bigger, not smaller
+						JPH_ASSERT(AABox(child_bounds_min[idx], child_bounds_max[idx]).Contains(child_nodes[idx]->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
+
+						// Add child to list of nodes to be processed
+						NodeData child;
+						child.mNode = child_nodes[idx];
+						child.mNodeBoundsMin = child_bounds_min[idx];
+						child.mNodeBoundsMax = child_bounds_max[idx];
+						child.mParentChildNodeStart = &node_data->mChildNodeStart[idx];
+						child.mParentTrianglesStart = &node_data->mChildTrianglesStart[idx];
+						NodeData *old = &node_list[0];
+						node_list.push_back(child);
+						if (old != &node_list[0])
+						{
+							outError = "Internal Error: Array reallocated, memory corruption!";
+							return false;
+						}
+
+						switch (inConvertMode)
+						{
+						case EAABBTreeToBufferConvertMode::DepthFirst:
+						case EAABBTreeToBufferConvertMode::BreadthFirst:
+							to_process.push_back(&node_list.back());
+							break;
+
+						case EAABBTreeToBufferConvertMode::DepthFirstTrianglesLast:
+						case EAABBTreeToBufferConvertMode::BreadthFirstTrianglesLast:
+							if (node_list.back().mNode->HasChildren())
+								to_process.push_back(&node_list.back());
+							else
+								to_process_triangles.push_back(&node_list.back());
+							break;
+						}
+					}
+				}
+				else
+				{				
+					// Add triangles
+					node_data->mTriangleStart = tri_ctx.Pack(inVertices, node_data->mNode->mTriangles, node_data->mNodeBoundsMin, node_data->mNodeBoundsMax, mTree, outError);
+					if (node_data->mTriangleStart == uint(-1))
+						return false;
+				}
+
+				// Patch offset into parent
+				if (node_data->mParentChildNodeStart != nullptr)
+				{
+					*node_data->mParentChildNodeStart = node_data->mNodeStart;
+					*node_data->mParentTrianglesStart = node_data->mTriangleStart;
+				}
+			}
+
+			// If we've got triangles to process, loop again with just the triangles
+			if (to_process_triangles.empty())
+				break;
+			else
+				to_process.swap(to_process_triangles);
+		}
+		
+		// Finalize all nodes
+		for (NodeData &n : node_list)
+			if (!node_ctx.NodeFinalize(n.mNode, n.mNodeStart, n.mTriangleStart, n.mNumChildren, n.mChildNodeStart, n.mChildTrianglesStart, mTree, outError))
+				return false;
+		
+		// Finalize the triangles
+		tri_ctx.Finalize(triangle_header, mTree);
+
+		// Get stats
+		tri_ctx.GetStats(outStats.mTriangleCodecName, outStats.mVerticesPerTriangle);
+
+		// Validate that we reserved enough memory
+		if (nodes_size < mNodesSize)
+		{
+			outError = "Internal Error: Not enough memory reserved for nodes!";
+			return false;
+		}
+		if (total_size < (uint)mTree.size())
+		{
+			outError = "Internal Error: Not enough memory reserved for triangles!";
+			return false;
+		}
+
+		// Finalize the nodes
+		if (!node_ctx.Finalize(header, inRoot, node_list[0].mNodeStart, node_list[0].mTriangleStart, outError))
+			return false;
+
+		// Shrink the tree, this will invalidate the header and triangle_header variables
+		mTree.shrink_to_fit();
+
+		// Output stats
+		outStats.mTotalSize = (uint)mTree.size();
+		outStats.mNodesSize = mNodesSize;
+		outStats.mTrianglesSize = (uint)mTree.size() - mNodesSize;
+		outStats.mBytesPerTriangle = (float)mTree.size() / tri_count;
+
+		return true;
+	}
+
+	/// Get resulting data
+	inline const ByteBuffer &		GetBuffer() const
+	{
+		return mTree;
+	}
+
+	/// Get resulting data
+	inline ByteBuffer &				GetBuffer()
+	{
+		return mTree;
+	}
+
+	/// Get header for tree
+	inline const NodeHeader *		GetNodeHeader() const
+	{
+		return mTree.Get<NodeHeader>(0);
+	}
+
+	/// Get header for triangles
+	inline const TriangleHeader *	GetTriangleHeader() const
+	{
+		return mTree.Get<TriangleHeader>(HeaderSize);
+	}
+
+	/// Get root of resulting tree
+	inline const void *				GetRoot() const
+	{
+		return mTree.Get<void>(HeaderSize + TriangleHeaderSize);
+	}
+	
+private:
+	ByteBuffer						mTree;									///< Resulting tree structure
+	uint							mNodesSize;								///< Size in bytes of the nodes in the buffer
+};
+
+} // JPH

+ 290 - 0
Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h

@@ -0,0 +1,290 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/ByteBuffer.h>
+#include <Math/HalfFloat.h>
+#include <AABBTree/AABBTreeBuilder.h>
+
+namespace JPH {
+
+template <int Alignment>
+class NodeCodecQuadTreeHalfFloat
+{
+public:
+	/// Number of child nodes of this node
+	static constexpr int				NumChildrenPerNode = 4;
+
+	/// Header for the tree
+	struct Header
+	{
+		Float3							mRootBoundsMin;
+		Float3							mRootBoundsMax;
+		uint32							mRootProperties;
+	};
+
+	/// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable)
+	static constexpr int				HeaderSize = sizeof(Header);
+	
+	/// Stack size to use during DecodingContext::sWalkTree
+	static constexpr int				StackSize = 128;
+
+	/// Node properties
+	enum : uint32
+	{
+		TRIANGLE_COUNT_BITS				= 4,
+		TRIANGLE_COUNT_SHIFT			= 28,
+		TRIANGLE_COUNT_MASK				= (1 << TRIANGLE_COUNT_BITS) - 1,
+		OFFSET_BITS						= 28,
+		OFFSET_MASK						= (1 << OFFSET_BITS) - 1,
+		OFFSET_NON_SIGNIFICANT_BITS		= 2,
+		OFFSET_NON_SIGNIFICANT_MASK		= (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1,
+	};
+
+	/// Node structure
+	struct Node
+	{
+		HalfFloat						mBoundsMinX[4];			///< 4 child bounding boxes
+		HalfFloat						mBoundsMinY[4];
+		HalfFloat						mBoundsMinZ[4];
+		HalfFloat						mBoundsMaxX[4];
+		HalfFloat						mBoundsMaxY[4];
+		HalfFloat						mBoundsMaxZ[4];
+		uint32							mNodeProperties[4];		///< 4 child node properties
+	};
+	
+	static_assert(sizeof(Node) == 64, "Node should be 64 bytes");
+
+	/// This class encodes and compresses quad tree nodes
+	class EncodingContext
+	{
+	public:
+		/// Get an upper bound on the amount of bytes needed for a node tree with inNodeCount nodes and inLeafNodeCount leaf nodes (those that contain the triangles)
+		uint							GetPessimisticMemoryEstimate(uint inNodeCount, uint inLeafNodeCount) const
+		{
+			return inNodeCount * (sizeof(Node) + Alignment - 1);
+		}
+
+		/// Allocate a new node for inNode. 
+		/// Algorithm can modify the order of ioChildren to indicate in which order children should be compressed
+		/// Algorithm can enlarge the bounding boxes of the children during compression and returns these in outChildBoundsMin, outChildBoundsMax
+		/// inNodeBoundsMin, inNodeBoundsMax is the bounding box if inNode possibly widened by compressing the parent node
+		/// Returns uint(-1) on error and reports the error in outError
+		uint							NodeAllocate(const AABBTreeBuilder::Node *inNode, Vec3Arg inNodeBoundsMin, Vec3Arg inNodeBoundsMax, vector<const AABBTreeBuilder::Node *> &ioChildren, Vec3 outChildBoundsMin[NumChildrenPerNode], Vec3 outChildBoundsMax[NumChildrenPerNode], ByteBuffer &ioBuffer, string &outError) const
+		{
+			// We don't emit nodes for leafs
+			if (!inNode->HasChildren())
+				return (uint)ioBuffer.size();
+				
+			// Align the buffer
+			ioBuffer.Align(Alignment);
+			uint node_start = (uint)ioBuffer.size();
+
+			// Fill in bounds
+			Node *node = ioBuffer.Allocate<Node>();
+
+			for (size_t i = 0; i < 4; ++i)
+			{
+				if (i < ioChildren.size())
+				{
+					const AABBTreeBuilder::Node *this_node = ioChildren[i];
+
+					// Copy bounding box
+					node->mBoundsMinX[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetX());
+					node->mBoundsMinY[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetY());
+					node->mBoundsMinZ[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetZ());
+					node->mBoundsMaxX[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetX());
+					node->mBoundsMaxY[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetY());
+					node->mBoundsMaxZ[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetZ());
+
+					// Store triangle count
+					node->mNodeProperties[i] = this_node->GetTriangleCount() << TRIANGLE_COUNT_SHIFT;
+					if (this_node->GetTriangleCount() >= TRIANGLE_COUNT_MASK)
+					{
+						outError = "NodeCodecQuadTreeHalfFloat: Too many triangles";
+						return uint(-1);
+					}
+				}
+				else
+				{
+					// Make this an invalid triangle node
+					node->mNodeProperties[i] = uint32(TRIANGLE_COUNT_MASK) << TRIANGLE_COUNT_SHIFT; 
+
+					// Make bounding box invalid
+					node->mBoundsMinX[i] = HALF_FLT_MAX;
+					node->mBoundsMinY[i] = HALF_FLT_MAX;
+					node->mBoundsMinZ[i] = HALF_FLT_MAX;
+					node->mBoundsMaxX[i] = HALF_FLT_MAX;
+					node->mBoundsMaxY[i] = HALF_FLT_MAX;
+					node->mBoundsMaxZ[i] = HALF_FLT_MAX;
+				}
+			}
+
+			// Since we don't keep track of the bounding box while descending the tree, we keep the root bounds at all levels for triangle compression
+			for (int i = 0; i < NumChildrenPerNode; ++i)
+			{
+				outChildBoundsMin[i] = inNodeBoundsMin;
+				outChildBoundsMax[i] = inNodeBoundsMax;
+			}
+
+			return node_start;
+		}
+
+		/// Once all nodes have been added, this call finalizes all nodes by patching in the offsets of the child nodes (that were added after the node itself was added)
+		bool						NodeFinalize(const AABBTreeBuilder::Node *inNode, uint inNodeStart, uint inTrianglesStart, uint inNumChildren, const uint *inChildrenNodeStart, const uint *inChildrenTrianglesStart, ByteBuffer &ioBuffer, string &outError) const
+		{
+			if (!inNode->HasChildren())
+				return true;
+
+			Node *node = ioBuffer.Get<Node>(inNodeStart);
+			for (uint i = 0; i < inNumChildren; ++i)
+			{
+				// If there are triangles, use the triangle offset otherwise use the node offset
+				uint offset = node->mNodeProperties[i] != 0? inChildrenTrianglesStart[i] : inChildrenNodeStart[i];
+				if (offset & OFFSET_NON_SIGNIFICANT_MASK)
+				{
+					outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-signifiant bits set";
+					return false;
+				}
+				offset >>= OFFSET_NON_SIGNIFICANT_BITS;
+				if (offset & ~OFFSET_MASK)
+				{
+					outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data.";
+					return false;
+				}
+
+				// Store offset of next node / triangles
+				node->mNodeProperties[i] |= offset;
+			}
+
+			return true;
+		}
+
+		/// Once all nodes have been finalized, this will finalize the header of the nodes
+		bool						Finalize(Header *outHeader, const AABBTreeBuilder::Node *inRoot, uint inRootNodeStart, uint inRootTrianglesStart, string &outError) const
+		{
+			uint offset = inRoot->HasChildren()? inRootNodeStart : inRootTrianglesStart;
+			if (offset & OFFSET_NON_SIGNIFICANT_MASK)
+			{
+				outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-signifiant bits set";
+				return false;
+			}
+			offset >>= OFFSET_NON_SIGNIFICANT_BITS;
+			if (offset & ~OFFSET_MASK)
+			{
+				outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data.";
+				return false;
+			}
+
+			inRoot->mBounds.mMin.StoreFloat3(&outHeader->mRootBoundsMin);
+			inRoot->mBounds.mMax.StoreFloat3(&outHeader->mRootBoundsMax);
+			outHeader->mRootProperties = offset + (inRoot->GetTriangleCount() << TRIANGLE_COUNT_SHIFT);
+			if (inRoot->GetTriangleCount() >= TRIANGLE_COUNT_MASK)
+			{
+				outError = "NodeCodecQuadTreeHalfFloat: Too many triangles";
+				return false;
+			}
+
+			return true;
+		}		
+	};
+
+	/// This class decodes and decompresses quad tree nodes
+	class DecodingContext
+	{
+	public:
+		/// Get the amount of bits needed to store an ID to a triangle block
+		inline static uint			sTriangleBlockIDBits(const ByteBuffer &inTree)
+		{
+			return 32 - CountLeadingZeros((uint32)inTree.size()) - OFFSET_NON_SIGNIFICANT_BITS;
+		}
+
+		/// Convert a triangle block ID to the start of the triangle buffer
+		inline static const void *	sGetTriangleBlockStart(const uint8 *inBufferStart, uint inTriangleBlockID)
+		{
+			return inBufferStart + (inTriangleBlockID << OFFSET_NON_SIGNIFICANT_BITS);
+		}
+
+		/// Constructor
+		inline						DecodingContext(const Header *inHeader) :
+			mRootBoundsMin(Vec3::sLoadFloat3Unsafe(inHeader->mRootBoundsMin)),
+			mRootBoundsMax(Vec3::sLoadFloat3Unsafe(inHeader->mRootBoundsMax))
+		{
+			// Start with the root node on the stack
+			mNodeStack[0] = inHeader->mRootProperties;
+		}
+
+		/// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitTriangles for each triangle encountered
+		template <class TriangleContext, class Visitor>
+		inline void					WalkTree(const uint8 *inBufferStart, const TriangleContext &inTriangleContext, Visitor &ioVisitor)
+		{
+			do
+			{
+				// Test if node contains triangles
+				uint32 node_properties = mNodeStack[mTop];
+				uint32 tri_count = node_properties >> TRIANGLE_COUNT_SHIFT;
+				if (tri_count == 0)
+				{
+					const Node *node = reinterpret_cast<const Node *>(inBufferStart + (node_properties << OFFSET_NON_SIGNIFICANT_BITS));
+
+					// Unpack bounds
+					UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMinX[0]));
+					Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy);
+					Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
+					
+					UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMinZ[0]));
+					Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx);
+					Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
+
+					UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMaxY[0]));
+					Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz);
+					Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
+
+					// Load properties for 4 children
+					UVec4 properties = UVec4::sLoadInt4(&node->mNodeProperties[0]);
+
+					// Check which sub nodes to visit
+					int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, mTop);
+
+					// Push them onto the stack
+					JPH_ASSERT(mTop + 4 < StackSize);
+					properties.StoreInt4(&mNodeStack[mTop]);
+					mTop += num_results;
+				}
+				else if (tri_count != TRIANGLE_COUNT_MASK) // TRIANGLE_COUNT_MASK indicates a padding node, normally we shouldn't visit these nodes but when querying with a big enough box you could touch HALF_FLT_MAX (about 65K)
+				{	
+					// Node contains triangles, do individual tests
+					uint32 triangle_block_id = node_properties & OFFSET_MASK;
+					const void *triangles = sGetTriangleBlockStart(inBufferStart, triangle_block_id);
+
+					ioVisitor.VisitTriangles(inTriangleContext, mRootBoundsMin, mRootBoundsMax, triangles, tri_count, triangle_block_id);
+				}
+
+				// Check if we're done
+				if (ioVisitor.ShouldAbort())
+					break;
+
+				// Fetch next node until we find one that the visitor wants to see
+				do 
+					--mTop;
+				while (mTop >= 0 && !ioVisitor.ShouldVisitNode(mTop));
+			}
+			while (mTop >= 0);
+		}
+
+		/// This can be used to have the visitor early out (ioVisitor.ShouldAbort() returns true) and later continue again (call WalkTree() again)
+		bool						IsDoneWalking() const
+		{
+			return mTop < 0;
+		}
+
+	private:
+		Vec3						mRootBoundsMin;
+		Vec3						mRootBoundsMax;
+		uint32						mNodeStack[StackSize];
+		int							mTop = 0;
+	};
+};
+
+} // JPH

+ 430 - 0
Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h

@@ -0,0 +1,430 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Geometry/RayTriangle.h>
+#include <unordered_map>
+
+namespace JPH {
+
+/// Store vertices in 64 bits and indices in 8 bits + 8 bit of flags per triangle like this:
+///
+/// TriangleBlockHeader,
+/// TriangleBlock (4 triangles and their flags in 16 bytes),
+/// TriangleBlock...
+///
+/// Vertices are stored:
+///
+/// VertexData (1 vertex in 64 bits),
+/// VertexData...
+///
+/// They're compressed relative to the bounding box as provided by the node codec.
+class TriangleCodecIndexed8BitPackSOA4Flags
+{
+public:
+	class TriangleHeader
+	{
+	public:
+		Float3						mOffset;			///< Offset of all vertices
+		Float3						mScale;				///< Scale of all vertices, vertex_position = mOffset + mScale * compressed_vertex_position
+	};
+
+	/// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable)
+	static constexpr int			TriangleHeaderSize = sizeof(TriangleHeader);
+
+	/// If this codec could return a different offset than the current buffer size when calling Pack()
+	static constexpr bool			ChangesOffsetOnPack = false; 
+
+	/// Amount of bits per component
+	enum EComponentData : uint32
+	{
+		COMPONENT_BITS = 21,
+		COMPONENT_MASK = (1 << COMPONENT_BITS) - 1,
+	};
+
+	/// Packed X and Y coordinate
+	enum EVertexXY : uint32
+	{
+		COMPONENT_X = 0,
+		COMPONENT_Y1 = COMPONENT_BITS,
+		COMPONENT_Y1_BITS = 32 - COMPONENT_BITS,
+	};
+
+	/// Packed Z and Y coordinate
+	enum EVertexZY : uint32
+	{
+		COMPONENT_Z = 0,
+		COMPONENT_Y2 = COMPONENT_BITS,
+		COMPONENT_Y2_BITS = 31 - COMPONENT_BITS,
+	};
+
+	/// A single packed vertex
+	struct VertexData
+	{
+		uint32						mVertexXY;
+		uint32						mVertexZY;
+	};
+
+	static_assert(sizeof(VertexData) == 8, "Compiler added padding");
+	
+	/// A block of 4 triangles
+	struct TriangleBlock
+	{
+		uint8						mIndices[3][4];				///< 8 bit indices to triangle vertices for 4 triangles in the form mIndices[vertex][triangle] where vertex in [0, 2] and triangle in [0, 3]
+		uint8						mFlags[4];					///< Triangle flags (could contain material and active edges)
+	};
+
+	static_assert(sizeof(TriangleBlock) == 16, "Compiler added padding");
+
+	/// A triangle header, will be followed by one or more TriangleBlocks
+	struct TriangleBlockHeader
+	{
+		const VertexData *			GetVertexData() const		{ return reinterpret_cast<const VertexData *>(reinterpret_cast<const uint8 *>(this) + mOffsetToVertices); }
+		const TriangleBlock *		GetTriangleBlock() const	{ return reinterpret_cast<const TriangleBlock *>(reinterpret_cast<const uint8 *>(this) + sizeof(TriangleBlockHeader)); }
+
+		uint32						mOffsetToVertices;			///< Offset from current block to start of vertices in bytes
+	};
+
+	static_assert(sizeof(TriangleBlockHeader) == 4, "Compiler added padding");
+
+	/// This class is used to encode and compress triangle data into a byte buffer
+	class EncodingContext
+	{
+	public:
+		EncodingContext() :
+			mNumTriangles(0)
+		{
+		}
+
+		/// Get an upper bound on the amount of bytes needed to store inTriangleCount triangles
+		uint						GetPessimisticMemoryEstimate(uint inTriangleCount) const
+		{
+			// Worst case each triangle is alone in a block, none of the vertices are shared and we need to add 3 bytes to align the vertices
+			return inTriangleCount * (sizeof(TriangleBlockHeader) + sizeof(TriangleBlock) + 3 * sizeof(VertexData)) + 3;
+		}
+
+		/// Pack the triangles in inContainer to ioBuffer. This stores the mMaterialIndex of a triangle in the 8 bit flags.
+		/// Returns uint(-1) on error.
+		uint						Pack(const VertexList &inVertices, const IndexedTriangleList &inTriangles, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, ByteBuffer &ioBuffer, string &outError)
+		{
+			// Determine position of triangles start
+			uint offset = (uint)ioBuffer.size();
+
+			// Update stats
+			uint tri_count = (uint)inTriangles.size();
+			mNumTriangles += tri_count;
+
+			// Allocate triangle block header
+			TriangleBlockHeader *header = ioBuffer.Allocate<TriangleBlockHeader>();
+
+			// Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared)
+			uint start_vertex = Clamp((int)mVertices.size() - 256 + (int)tri_count * 3, 0, (int)mVertices.size());
+
+			// Store the start vertex offset, this will later be patched to give the delta offset relative to the triangle block
+			mOffsetsToPatch.push_back(uint((uint8 *)&header->mOffsetToVertices - (uint8 *)&ioBuffer[0]));
+			header->mOffsetToVertices = start_vertex * sizeof(VertexData);
+
+			// Pack vertices
+			uint padded_triangle_count = AlignUp(tri_count, 4);
+			for (uint t = 0; t < padded_triangle_count; t += 4)
+			{
+				TriangleBlock *block = ioBuffer.Allocate<TriangleBlock>();
+				for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr)
+					for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx)
+					{
+						// Fetch vertex index. Create degenerate triangles for padding triangles.
+						bool triangle_available = t + block_tri_idx < tri_count;
+						uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[tri_count - 1].mIdx[0];
+
+						// Check if we've seen this vertex before and if it is in the range that we can encode
+						uint32 vertex_index;
+						VertexMap::const_iterator found = mVertexMap.find(src_vertex_index);
+						if (found == mVertexMap.end() || found->second < start_vertex)
+						{
+							// Add vertex
+							vertex_index = (uint32)mVertices.size();
+							mVertexMap[src_vertex_index] = vertex_index;
+							mVertices.push_back(inVertices[src_vertex_index]);
+						}
+						else
+						{
+							// Reuse vertex
+							vertex_index = found->second;
+						}
+
+						// Store vertex index
+						uint32 vertex_offset = vertex_index - start_vertex;
+						if (vertex_offset > 0xff)
+						{
+							outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset doesn't fit in 8 bit";
+							return uint(-1);
+						}
+						block->mIndices[vertex_nr][block_tri_idx] = (uint8)vertex_offset;											   						
+
+						// Store flags
+						uint32 flags = triangle_available? inTriangles[t + block_tri_idx].mMaterialIndex : 0;
+						if (flags > 0xff)
+						{
+							outError = "TriangleCodecIndexed8BitPackSOA4Flags: Material index doesn't fit in 8 bit";
+							return uint(-1);
+						}
+						block->mFlags[block_tri_idx] = (uint8)flags;
+					}
+			}
+
+			return offset;
+		}
+
+		/// After all triangles have been packed, this finalizes the header and triangle buffer
+		void						Finalize(TriangleHeader *ioHeader, ByteBuffer &ioBuffer) const
+		{
+			// Check if anything to do
+			if (mVertices.empty())
+				return;
+
+			// Align buffer to 4 bytes
+			uint vertices_idx = (uint)ioBuffer.Align(4);
+
+			// Patch the offsets
+			for (uint o : mOffsetsToPatch)
+				*ioBuffer.Get<uint32>(o) += vertices_idx - o;
+
+			// Calculate bounding box
+			AABox bounds;
+			for (const Float3 &v : mVertices)
+				bounds.Encapsulate(Vec3(v));
+
+			// Compress vertices
+			VertexData *vertices = ioBuffer.Allocate<VertexData>(mVertices.size());
+			Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(bounds.GetSize(), Vec3::sReplicate(1.0e-20f));
+			for (const Float3 &v : mVertices)
+			{
+				UVec4 c = ((Vec3(v) - bounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt();
+				JPH_ASSERT(c.GetX() <= COMPONENT_MASK);
+				JPH_ASSERT(c.GetY() <= COMPONENT_MASK);
+				JPH_ASSERT(c.GetZ() <= COMPONENT_MASK);
+				vertices->mVertexXY = c.GetX() + (c.GetY() << COMPONENT_Y1);
+				vertices->mVertexZY = c.GetZ() + ((c.GetY() >> COMPONENT_Y1_BITS) << COMPONENT_Y2);
+				++vertices;
+			}
+
+			// Store decompression information
+			bounds.mMin.StoreFloat3(&ioHeader->mOffset);
+			(bounds.GetSize() / Vec3::sReplicate(COMPONENT_MASK)).StoreFloat3(&ioHeader->mScale);
+		}
+
+		void						GetStats(string &outTriangleCodecName, float &outVerticesPerTriangle)
+		{
+			// Store stats
+			outTriangleCodecName = "Indexed8BitPackSOA4";
+			outVerticesPerTriangle = (float)mVertices.size() / mNumTriangles;
+		}
+
+	private:
+		using VertexMap = unordered_map<uint32, uint32>;
+
+		uint						mNumTriangles;
+		vector<Float3>				mVertices;				///< Output vertices, sorted according to occurrence
+		VertexMap					mVertexMap;				///< Maps from the original mesh vertex index (inVertices) to the index in our output vertices (mVertices)
+		vector<uint>				mOffsetsToPatch;		///< Offsets to the vertex buffer that need to be patched in once all nodes have been packed
+	};
+
+	/// This class is used to decode and decompress triangle data packed by the EncodingContext
+	class DecodingContext
+	{
+	private:
+		/// Private helper functions to unpack the 1 vertex of 4 triangles (outX contains the x coordinate of triangle 0 .. 3 etc.)
+		JPH_INLINE void				Unpack(const VertexData *inVertices, UVec4 inIndex, Vec4 &outX, Vec4 &outY, Vec4 &outZ) const
+		{
+			// Get compressed data
+			UVec4 c1 = UVec4::sGatherInt4<8>(&inVertices->mVertexXY, inIndex);
+			UVec4 c2 = UVec4::sGatherInt4<8>(&inVertices->mVertexZY, inIndex);
+
+			// Unpack the x y and z component
+			UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK));
+			UVec4 yc = UVec4::sOr(c1.LogicalShiftRight<COMPONENT_Y1>(), c2.LogicalShiftRight<COMPONENT_Y2>().LogicalShiftLeft<COMPONENT_Y1_BITS>());
+			UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK));
+
+			// Convert to float
+			outX = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX);
+			outY = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY);
+			outZ = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ);
+		}
+
+	public:
+		JPH_INLINE					DecodingContext(const TriangleHeader *inHeader, const ByteBuffer &inBuffer) :
+			mOffsetX(Vec4::sReplicate(inHeader->mOffset.x)),
+			mOffsetY(Vec4::sReplicate(inHeader->mOffset.y)),
+			mOffsetZ(Vec4::sReplicate(inHeader->mOffset.z)),
+			mScaleX(Vec4::sReplicate(inHeader->mScale.x)),
+			mScaleY(Vec4::sReplicate(inHeader->mScale.y)),
+			mScaleZ(Vec4::sReplicate(inHeader->mScale.z))
+		{
+		}
+
+		/// Unpacks triangles in the format t1v1,t1v2,t1v3, t2v1,t2v2,t2v3, ...
+		JPH_INLINE void				Unpack(Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles) const
+		{
+			JPH_ASSERT(inNumTriangles > 0);
+			const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
+			const VertexData *vertices = header->GetVertexData();			
+			const TriangleBlock *t = header->GetTriangleBlock();
+			const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
+			
+			int triangles_left = inNumTriangles;
+
+			do
+			{
+				// Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok)
+				UVec4 indices = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&t->mIndices[0]));
+				UVec4 iv1 = indices.Expand4Byte0();
+				UVec4 iv2 = indices.Expand4Byte4();
+				UVec4 iv3 = indices.Expand4Byte8();
+
+				// Decompress the triangle data
+				Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z;
+				Unpack(vertices, iv1, v1x, v1y, v1z);
+				Unpack(vertices, iv2, v2x, v2y, v2z);
+				Unpack(vertices, iv3, v3x, v3y, v3z);
+
+				// Transpose it so we get normal vectors
+				Mat44 v1 = Mat44(v1x, v1y, v1z, Vec4::sZero()).Transposed();
+				Mat44 v2 = Mat44(v2x, v2y, v2z, Vec4::sZero()).Transposed();
+				Mat44 v3 = Mat44(v3x, v3y, v3z, Vec4::sZero()).Transposed();
+
+				// Store triangle data
+				for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left)
+				{
+					*outTriangles++ = v1.GetColumn3(i);
+					*outTriangles++ = v2.GetColumn3(i);
+					*outTriangles++ = v3.GetColumn3(i);
+				}
+
+				++t;
+			} 
+			while (t < end);
+		}
+
+		/// Tests a ray against the packed triangles
+		JPH_INLINE float			TestRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, const void *inTriangleStart, uint32 inNumTriangles, float inClosest, uint32 &outClosestTriangleIndex) const
+		{
+			JPH_ASSERT(inNumTriangles > 0);
+			const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
+			const VertexData *vertices = header->GetVertexData();			
+			const TriangleBlock *t = header->GetTriangleBlock();
+			const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
+
+			Vec4 closest = Vec4::sReplicate(inClosest);
+			UVec4 closest_triangle_idx = UVec4::sZero();
+
+			UVec4 start_triangle_idx = UVec4::sZero();
+			do
+			{
+				// Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok)
+				UVec4 indices = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&t->mIndices[0]));
+				UVec4 iv1 = indices.Expand4Byte0();
+				UVec4 iv2 = indices.Expand4Byte4();
+				UVec4 iv3 = indices.Expand4Byte8();
+
+				// Decompress the triangle data
+				Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z;
+				Unpack(vertices, iv1, v1x, v1y, v1z);
+				Unpack(vertices, iv2, v2x, v2y, v2z);
+				Unpack(vertices, iv3, v3x, v3y, v3z);
+
+				// Perform ray vs triangle test
+				Vec4 distance = RayTriangle4(inRayOrigin, inRayDirection, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);
+
+				// Update closest with the smaller values
+				UVec4 smaller = Vec4::sLess(distance, closest);
+				closest = Vec4::sSelect(closest, distance, smaller);
+
+				// Update triangle index with the smallest values
+				UVec4 triangle_idx = start_triangle_idx + UVec4(0, 1, 2, 3);
+				closest_triangle_idx = UVec4::sSelect(closest_triangle_idx, triangle_idx, smaller);
+
+				// Next block
+				++t;
+				start_triangle_idx += UVec4::sReplicate(4);
+			} 
+			while (t < end);
+
+			// Get the smallest component
+			Vec4::sSort4(closest, closest_triangle_idx);
+			outClosestTriangleIndex = closest_triangle_idx.GetX();
+			return closest.GetX();
+		}
+
+		/// Decode a single triangle
+		inline void					GetTriangle(const void *inTriangleStart, uint32 inTriangleIdx, Vec3 &outV1, Vec3 &outV2, Vec3 &outV3) const
+		{
+			const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
+			const VertexData *vertices = header->GetVertexData();
+			const TriangleBlock *block = header->GetTriangleBlock() + (inTriangleIdx >> 2);
+			uint32 block_triangle_idx = inTriangleIdx & 0b11;
+
+			// Get the 3 vertices
+			const VertexData &v1 = vertices[block->mIndices[0][block_triangle_idx]];
+			const VertexData &v2 = vertices[block->mIndices[1][block_triangle_idx]];
+			const VertexData &v3 = vertices[block->mIndices[2][block_triangle_idx]];
+
+			// Pack the vertices
+			UVec4 c1(v1.mVertexXY, v2.mVertexXY, v3.mVertexXY, 0);
+			UVec4 c2(v1.mVertexZY, v2.mVertexZY, v3.mVertexZY, 0);
+
+			// Unpack the x y and z component
+			UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK));
+			UVec4 yc = UVec4::sOr(c1.LogicalShiftRight<COMPONENT_Y1>(), c2.LogicalShiftRight<COMPONENT_Y2>().LogicalShiftLeft<COMPONENT_Y1_BITS>());
+			UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK));
+
+			// Convert to float
+			Vec4 vx = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX);
+			Vec4 vy = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY);
+			Vec4 vz = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ);
+
+			// Transpose it so we get normal vectors
+			Mat44 trans = Mat44(vx, vy, vz, Vec4::sZero()).Transposed();
+			outV1 = trans.GetAxisX();
+			outV2 = trans.GetAxisY();
+			outV3 = trans.GetAxisZ();
+		}
+
+		/// Get flags for entire triangle block
+		JPH_INLINE static void		sGetFlags(const void *inTriangleStart, uint32 inNumTriangles, uint8 *outTriangleFlags) 
+		{
+			JPH_ASSERT(inNumTriangles > 0);
+			const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
+			const TriangleBlock *t = header->GetTriangleBlock();
+			const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
+			
+			int triangles_left = inNumTriangles;
+			do
+			{
+				for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left)
+					*outTriangleFlags++ = t->mFlags[i];
+
+				++t;
+			} 
+			while (t < end);
+		}
+
+		/// Get flags for a particular triangle
+		JPH_INLINE static uint8		sGetFlags(const void *inTriangleStart, int inTriangleIndex)
+		{
+			const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
+			const TriangleBlock *first_block = header->GetTriangleBlock();
+			return first_block[inTriangleIndex >> 2].mFlags[inTriangleIndex & 0b11];
+		}
+
+	private:
+		Vec4						mOffsetX;
+		Vec4						mOffsetY;
+		Vec4						mOffsetZ;
+		Vec4						mScaleX;
+		Vec4						mScaleY;
+		Vec4						mScaleZ;
+	};
+};
+
+} // JPH

+ 67 - 0
Jolt/Core/AlignedAllocator.h

@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/Memory.h>
+
+namespace JPH {
+
+/// STL allocator that takes care that memory is aligned to N bytes
+template <typename T, size_t N>
+class AlignedAllocator
+{
+public:
+	using value_type = T;
+
+	/// Pointer to type
+	using pointer = T *;
+	using const_pointer = const T *;
+
+	/// Reference to type.
+	/// Can be removed in C++20.
+	using reference = T &;
+	using const_reference = const T &;
+
+	using size_type = size_t;
+	using difference_type = ptrdiff_t;
+
+	/// Constructor
+	inline					AlignedAllocator() = default;
+
+	/// Constructor from other allocator
+	template <typename T2>
+	inline					AlignedAllocator(const AlignedAllocator<T2, N> &) { }
+
+	/// Allocate memory
+	inline pointer			allocate(size_type n)
+	{
+		return (pointer)AlignedAlloc(n * sizeof(value_type), N);
+	}
+
+	/// Free memory
+	inline void				deallocate(pointer p, size_type)
+	{
+		AlignedFree(p);
+	}
+
+	/// Allocators are stateless so assumed to be equal
+	inline bool				operator == (const AlignedAllocator<T, N>& other) const
+	{
+		return true;
+	}
+
+	inline bool				operator != (const AlignedAllocator<T, N>& other) const
+	{
+		return false;
+	}
+
+	/// Converting to allocator for other type
+	template <typename T2>
+	struct rebind
+	{
+		using other = AlignedAllocator<T2, N>;
+	};
+};
+
+} // JPH

+ 32 - 0
Jolt/Core/Atomics.h

@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <atomic>
+
+namespace JPH {
+
+/// Atomically compute the min(ioAtomic, inValue) and store it in ioAtomic, returns true if value was updated
+template <class T>
+bool AtomicMin(atomic<T> &ioAtomic, const T inValue)
+{
+	T cur_value = ioAtomic;
+	while (cur_value > inValue)
+		if (ioAtomic.compare_exchange_weak(cur_value, inValue))
+			return true;
+	return false;
+}
+
+/// Atomically compute the max(ioAtomic, inValue) and store it in ioAtomic, returns true if value was updated
+template <class T>
+bool AtomicMax(atomic<T> &ioAtomic, const T inValue)
+{
+	T cur_value = ioAtomic;
+	while (cur_value < inValue)
+		if (ioAtomic.compare_exchange_weak(cur_value, inValue))
+			return true;
+	return false;
+}
+
+} // JPH

+ 73 - 0
Jolt/Core/ByteBuffer.h

@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/AlignedAllocator.h>
+
+namespace JPH {
+
+/// Underlying data type for ByteBuffer
+using ByteBufferVector = vector<uint8, AlignedAllocator<uint8, JPH_CACHE_LINE_SIZE>>;
+
+/// Simple byte buffer, aligned to a cache line
+class ByteBuffer : public ByteBufferVector
+{
+public:
+	/// Align the size to a multiple of inSize, returns the length after alignment
+	size_t			Align(size_t inSize)
+	{
+		// Assert power of 2
+		JPH_ASSERT(IsPowerOf2(inSize));
+
+		// Calculate new size and resize buffer
+		size_t s = AlignUp(size(), inSize);
+		resize(s);
+
+		return s;
+	}
+
+	/// Allocate block of data of inSize elements and return the pointer
+	template <class Type>
+	Type *			Allocate(size_t inSize = 1)
+	{
+		// Reserve space
+		size_t s = size();
+		resize(s + inSize * sizeof(Type));
+
+		// Get data pointer
+		Type *data = reinterpret_cast<Type *>(&at(s));
+
+		// Construct elements
+		for (Type *d = data, *d_end = data + inSize; d < d_end; ++d)
+			new (d) Type;
+
+		// Return pointer
+		return data;
+	}
+
+	/// Append inData to the buffer
+	template <class Type>
+	void			AppendVector(const vector<Type> &inData)
+	{
+		size_t size = inData.size() * sizeof(Type);
+		uint8 *data = Allocate<uint8>(size);
+		memcpy(data, &inData[0], size);
+	}
+
+	/// Get object at inPosition (an offset in bytes)
+	template <class Type>
+	const Type *	Get(size_t inPosition) const
+	{
+		return reinterpret_cast<const Type *>(&at(inPosition));
+	}
+
+	/// Get object at inPosition (an offset in bytes)
+	template <class Type>
+	Type *			Get(size_t inPosition)
+	{
+		return reinterpret_cast<Type *>(&at(inPosition));
+	}
+};
+
+} // JPH

+ 37 - 0
Jolt/Core/Color.cpp

@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/Color.h>
+
+namespace JPH {
+
+// Predefined colors
+const Color Color::sBlack(0, 0, 0);
+const Color Color::sDarkRed(128, 0, 0);
+const Color Color::sRed(255, 0, 0);
+const Color Color::sDarkGreen(0, 128, 0);
+const Color Color::sGreen(0, 255, 0);
+const Color Color::sDarkBlue(0, 0, 128);
+const Color Color::sBlue(0, 0, 255);
+const Color Color::sYellow(255, 255, 0);
+const Color Color::sPurple(255, 0, 255);
+const Color Color::sCyan(0, 255, 255);
+const Color Color::sOrange(255, 128, 0);
+const Color Color::sDarkOrange(128, 64, 0);
+const Color Color::sGrey(128, 128, 128);
+const Color Color::sLightGrey(192, 192, 192);
+const Color Color::sWhite(255, 255, 255);
+
+// Generated by: http://phrogz.net/css/distinct-colors.html (this algo: https://en.wikipedia.org/wiki/Color_difference#CMC_l:c_.281984.29)
+static Color sColors[] = { Color(255, 0, 0), Color(204, 143, 102), Color(226, 242, 0), Color(41, 166, 124), Color(0, 170, 255), Color(69, 38, 153), Color(153, 38, 130), Color(229, 57, 80), Color(204, 0, 0), Color(255, 170, 0), Color(85, 128, 0), Color(64, 255, 217), Color(0, 75, 140), Color(161, 115, 230), Color(242, 61, 157), Color(178, 101, 89), Color(140, 94, 0), Color(181, 217, 108), Color(64, 242, 255), Color(77, 117, 153), Color(157, 61, 242), Color(140, 0, 56), Color(127, 57, 32), Color(204, 173, 51), Color(64, 255, 64), Color(38, 145, 153), Color(0, 102, 255), Color(242, 0, 226), Color(153, 77, 107), Color(229, 92, 0), Color(140, 126, 70), Color(0, 179, 71), Color(0, 194, 242), Color(27, 0, 204), Color(230, 115, 222), Color(127, 0, 17) };
+
+Color Color::sGetDistinctColor(int inIndex)
+{
+	JPH_ASSERT(inIndex >= 0);
+
+	return sColors[inIndex % (sizeof(sColors) / sizeof(uint32))];
+}
+
+} // JPH

+ 76 - 0
Jolt/Core/Color.h

@@ -0,0 +1,76 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+class Color;
+
+/// Type to use for passing arguments to a function
+using ColorArg = Color;
+
+/// Class that holds an RGBA color with 8-bits per component
+class [[nodiscard]] Color
+{
+public:
+	/// Constructors
+							Color() = default; ///< Intentionally not initialized for performance reasons
+							Color(const Color &inRHS) = default;
+	explicit				Color(uint32 inColor)													: mU32(inColor) { }
+							Color(uint8 inRed, uint8 inGreen, uint8 inBlue, uint8 inAlpha = 255)	: r(inRed), g(inGreen), b(inBlue), a(inAlpha) { }
+							Color(ColorArg inRHS, uint8 inAlpha)									: r(inRHS.r), g(inRHS.g), b(inRHS.b), a(inAlpha) { }
+											
+	/// Comparison			
+	inline bool				operator == (ColorArg inRHS) const										{ return mU32 == inRHS.mU32; }
+	inline bool				operator != (ColorArg inRHS) const										{ return mU32 != inRHS.mU32; }
+	
+	/// Convert to uint32
+	uint32					GetUInt32() const														{ return mU32; }
+
+	/// Element access, 0 = red, 1 = green, 2 = blue, 3 = alpha
+	inline uint8			operator () (uint inIdx) const											{ JPH_ASSERT(inIdx < 4); return (&r)[inIdx]; }
+	inline uint8 &			operator () (uint inIdx)												{ JPH_ASSERT(inIdx < 4); return (&r)[inIdx]; }
+
+	/// Convert to Vec4 with range [0, 1]
+	inline Vec4				ToVec4() const															{ return Vec4(r, g, b, a) / 255.0f; }
+
+	/// Get grayscale intensity of color
+	inline uint8			GetIntensity() const													{ return uint8((uint32(r) * 54 + g * 183 + b * 19) >> 8); }
+
+	/// Get a visually distinct color
+	static Color			sGetDistinctColor(int inIndex);
+
+	/// Predefined colors
+	static const Color		sBlack;
+	static const Color		sDarkRed;
+	static const Color		sRed;
+	static const Color		sDarkGreen;
+	static const Color		sGreen;
+	static const Color		sDarkBlue;
+	static const Color		sBlue;
+	static const Color		sYellow;
+	static const Color		sPurple;
+	static const Color		sCyan;
+	static const Color		sOrange;
+	static const Color		sDarkOrange;
+	static const Color		sGrey;
+	static const Color		sLightGrey;
+	static const Color		sWhite;
+
+	union
+	{
+		uint32				mU32;																	///< Combined value for red, green, blue and alpha
+		struct
+		{
+			uint8			r;																		///< Red channel
+			uint8			g;																		///< Green channel
+			uint8			b;																		///< Blue channel
+			uint8			a;																		///< Alpha channel
+		};
+	};
+};
+
+static_assert(is_trivial<Color>(), "Is supposed to be a trivial type!");
+
+} // JPH

+ 214 - 0
Jolt/Core/Core.h

@@ -0,0 +1,214 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+// Determine platform
+#if defined(JPH_PLATFORM_BLUE)
+	// Correct define already defined, this overrides everything else
+#elif defined(_WIN32) || defined(_WIN64)
+	#define JPH_PLATFORM_WINDOWS
+#elif defined(__ANDROID__) // Android is linux too, so that's why we check it first
+	#define JPH_PLATFORM_ANDROID
+#elif defined(__linux__)
+	#define JPH_PLATFORM_LINUX
+#endif
+
+// Turn off warnings
+#if defined(__clang__)
+	#pragma clang diagnostic ignored "-Wc++98-compat"
+	#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+	#pragma clang diagnostic ignored "-Wfloat-equal"
+	#pragma clang diagnostic ignored "-Wnewline-eof"
+	#pragma clang diagnostic ignored "-Wsign-conversion"
+	#pragma clang diagnostic ignored "-Wold-style-cast"
+	#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+	#pragma clang diagnostic ignored "-Wnested-anon-types"
+	#pragma clang diagnostic ignored "-Wglobal-constructors"
+	#pragma clang diagnostic ignored "-Wexit-time-destructors"
+	#pragma clang diagnostic ignored "-Wnonportable-system-include-path"
+	#pragma clang diagnostic ignored "-Wlanguage-extension-token"
+	#pragma clang diagnostic ignored "-Wunused-parameter"
+	#pragma clang diagnostic ignored "-Wformat-nonliteral"
+	#pragma clang diagnostic ignored "-Wcovered-switch-default"
+	#pragma clang diagnostic ignored "-Wcast-align"
+	#pragma clang diagnostic ignored "-Winvalid-offsetof"
+	#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+	#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
+	#ifndef JPH_PLATFORM_ANDROID
+		#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
+	#endif
+#elif defined(_MSC_VER)
+	#pragma warning (disable : 4514) // 'X' : unreferenced inline function has been removed
+	#pragma warning (disable : 4710) // 'X' : function not inlined
+	#pragma warning (disable : 4711) // function 'X' selected for automatic inline expansion
+	#pragma warning (disable : 4820) // 'X': 'Y' bytes padding added after data member 'Z'
+	#pragma warning (disable : 4100) // 'X' : unreferenced formal parameter
+	#pragma warning (disable : 4626) // 'X' : assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted
+	#pragma warning (disable : 5027) // 'X' : move assignment operator was implicitly defined as deleted because a base class move assignment operator is inaccessible or deleted
+	#pragma warning (disable : 4365) // 'argument' : conversion from 'X' to 'Y', signed / unsigned mismatch
+	#pragma warning (disable : 4324) // 'X' : structure was padded due to alignment specifier
+	#pragma warning (disable : 4625) // 'X' : copy constructor was implicitly defined as deleted because a base class copy constructor is inaccessible or deleted
+	#pragma warning (disable : 5026) // 'X': move constructor was implicitly defined as deleted because a base class move constructor is inaccessible or deleted
+	#pragma warning (disable : 4623) // 'X' : default constructor was implicitly defined as deleted
+	#pragma warning (disable : 4201) // nonstandard extension used: nameless struct/union
+	#pragma warning (disable : 4371) // 'X': layout of class may have changed from a previous version of the compiler due to better packing of member 'Y'
+	#pragma warning (disable : 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
+	#pragma warning (disable : 4583) // 'X': destructor is not implicitly called
+	#pragma warning (disable : 4582) // 'X': constructor is not implicitly called
+	#pragma warning (disable : 5219) // implicit conversion from 'X' to 'Y', possible loss of data 
+#endif
+
+// Detect CPU architecture
+#if defined(__x86_64__) || defined(_M_X64)
+	// X86 CPU architecture
+	#define JPH_CPU_X64
+	#define JPH_USE_SSE
+
+	// Detect enabled instruction sets
+	#if (defined(__F16C__) || defined(__AVX2__)) && !defined(JPH_USE_F16C)
+		#define JPH_USE_F16C
+	#endif
+	#if (defined(__LZCNT__) || defined(__AVX2__)) && !defined(JPH_USE_LZCNT)
+		#define JPH_USE_LZCNT
+	#endif
+	#if defined(__AVX__) && !defined(JPH_USE_AVX)
+		#define JPH_USE_AVX
+	#endif
+	#if defined(__AVX2__) && !defined(JPH_USE_AVX2)
+		#define JPH_USE_AVX2
+	#endif
+	#if defined(__clang__)
+		#if defined(__FMA__) && !defined(JPH_USE_FMADD)
+			#define JPH_USE_FMADD
+		#endif
+	#elif defined(_MSC_VER)
+		#if defined(__AVX2__) && !defined(JPH_USE_FMADD) // AVX2 also enables fused multiply add
+			#define JPH_USE_FMADD
+		#endif
+	#else
+		#error Undefined compiler
+	#endif
+#elif defined(__aarch64__) || defined(_M_ARM64)
+	// ARM64 CPU architecture
+	#define JPH_CPU_ARM64
+	#define JPH_USE_NEON
+#else
+	#error Unsupported CPU architecture
+#endif
+
+// OS-specific includes
+#if defined(JPH_PLATFORM_WINDOWS)
+	#define JPH_BREAKPOINT		__debugbreak()
+#elif defined(JPH_PLATFORM_BLUE) 
+	// Configuration for a popular game console.
+	// This file is not distributed because it would violate an NDA. 
+	// Creating one should only be a couple of minutes of work if you have the documentation for the platform 
+	// (you only need to define JPH_BREAKPOINT, JPH_PLATFORM_BLUE_GET_TICKS and JPH_PLATFORM_BLUE_GET_TICK_FREQUENCY and include the right header).
+	#include <Core/PlatformBlue.h> 
+#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID)
+	#include <float.h>
+	#include <limits.h>
+	#include <string.h>
+
+	#if defined(JPH_CPU_X64)
+		#define JPH_BREAKPOINT		__asm volatile ("int $0x3")
+	#elif defined(JPH_CPU_ARM64)
+		#define JPH_BREAKPOINT		__builtin_trap()
+	#endif
+#else
+	#error Unknown platform
+#endif
+
+// Crashes the application
+#define JPH_CRASH				do { int *ptr = nullptr; *ptr = 0; } while (false)
+
+// Standard C++ includes
+#include <vector>
+#include <algorithm>
+#include <utility>
+#include <cmath>
+#include <sstream>
+#include <functional>
+#if defined(JPH_USE_SSE)
+	#include <immintrin.h>
+#elif defined(JPH_USE_NEON)
+	#include <arm_neon.h>
+#endif
+
+namespace JPH {
+
+using namespace std;
+
+// Standard types
+using uint = unsigned int;
+using uint8 = uint8_t;
+using uint16 = uint16_t;
+using uint32 = uint32_t;
+using uint64 = uint64_t;
+
+// Assert sizes of types
+static_assert(sizeof(uint) >= 4, "Invalid size of uint");
+static_assert(sizeof(uint8) == 1, "Invalid size of uint8");
+static_assert(sizeof(uint16) == 2, "Invalid size of uint16");
+static_assert(sizeof(uint32) == 4, "Invalid size of uint32");
+static_assert(sizeof(uint64) == 8, "Invalid size of uint64");
+static_assert(sizeof(void *) == 8, "Invalid size of pointer");
+
+// Define inline macro
+#if defined(__clang__)
+	#define JPH_INLINE __inline__ __attribute__((always_inline))
+#elif defined(_MSC_VER)
+	#define JPH_INLINE __forceinline
+#else
+	#error Undefined
+#endif
+
+// Cache line size (used for aligning to cache line)
+#ifndef JPH_CACHE_LINE_SIZE
+	#define JPH_CACHE_LINE_SIZE 64
+#endif
+
+// Define macro to get current function name
+#if defined(__clang__)
+	#define JPH_FUNCTION_NAME	__PRETTY_FUNCTION__
+#elif defined(_MSC_VER)
+	#define JPH_FUNCTION_NAME	__FUNCTION__
+#else
+	#error Undefined
+#endif
+
+// Stack allocation
+#define JPH_STACK_ALLOC(n)		alloca(n)
+	
+// Shorthand for #ifdef _DEBUG / #endif
+#ifdef _DEBUG
+	#define JPH_IF_DEBUG(...)	__VA_ARGS__
+#else
+	#define JPH_IF_DEBUG(...)
+#endif
+
+// Macro to indicate that a parameter / variable is unused
+#define JPH_UNUSED(x)			(void)x
+
+// Macro to enable floating point precise mode and to disable fused multiply add instructions
+#if defined(__clang__)
+	// In clang it appears you cannot turn off -ffast-math and -ffp-contract=fast for a code block
+	// There is #pragma clang fp contract (off) but that doesn't seem to work under clang 9 & 10 when -ffast-math is specified on the commandline (you override it to turn it on, but not off)
+	// There is #pragma float_control(precise, on) but that doesn't work under clang 9.
+	// So for now we compile clang without -ffast-math so the macros are empty
+	#define JPH_PRECISE_MATH_ON
+	#define JPH_PRECISE_MATH_OFF
+#elif defined(_MSC_VER)
+	// Unfortunately there is no way to push the state of fp_contract, so we have to assume it was turned on before JPH_PRECISE_MATH_ON
+	#define JPH_PRECISE_MATH_ON						\
+		__pragma(float_control(precise, on, push))	\
+		__pragma(fp_contract(off))
+	#define JPH_PRECISE_MATH_OFF					\
+		__pragma(fp_contract(on))					\
+		__pragma(float_control(pop))
+#else
+	#error Undefined
+#endif
+
+} // JPH

+ 70 - 0
Jolt/Core/FPControlWord.h

@@ -0,0 +1,70 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/NonCopyable.h>
+
+namespace JPH {
+
+#ifdef JPH_USE_SSE
+
+/// Helper class that needs to be put on the stack to update the state of the floating point control word.
+/// This state is kept per thread.
+template <uint Value, uint Mask>
+class FPControlWord : public NonCopyable
+{
+public:
+				FPControlWord()
+	{
+		mPrevState = _mm_getcsr();
+		_mm_setcsr((mPrevState & ~Mask) | Value);
+	}
+
+				~FPControlWord()
+	{
+		_mm_setcsr((_mm_getcsr() & ~Mask) | (mPrevState & Mask));
+	}
+
+private:
+	uint		mPrevState;	
+};
+
+#elif defined(JPH_USE_NEON)
+
+/// Helper class that needs to be put on the stack to update the state of the floating point control word.
+/// This state is kept per thread.
+template <uint64 Value, uint64 Mask>
+class FPControlWord : public NonCopyable
+{
+public:
+				FPControlWord()
+	{
+		uint64 val;
+	    asm volatile("mrs %0, fpcr" : "=r" (val));
+		mPrevState = val;
+		val &= ~Mask;
+		val |= Value;
+	    asm volatile("msr fpcr, %0" : "=r" (val));
+	}
+
+				~FPControlWord()
+	{
+		uint64 val;
+		asm volatile("mrs %0, fpcr" : "=r" (val));
+		val &= ~Mask;
+		val |= mPrevState & Mask;
+		asm volatile("msr fpcr, %0" : "=r" (val));
+	}
+
+private:
+	uint64		mPrevState;
+};
+
+#else
+
+#error Unsupported CPU architecture
+
+#endif
+
+} // JPH

+ 55 - 0
Jolt/Core/FPException.h

@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/FPControlWord.h>
+
+namespace JPH {
+
+#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
+
+#if defined(JPH_USE_SSE)
+
+/// Enable floating point divide by zero exception and exceptions on invalid numbers
+class FPExceptionsEnable : public FPControlWord<0, _MM_MASK_DIV_ZERO | _MM_MASK_INVALID> { };
+
+/// Disable invalid floating point value exceptions
+class FPExceptionDisableInvalid : public FPControlWord<_MM_MASK_INVALID, _MM_MASK_INVALID> { };
+
+/// Disable division by zero floating point exceptions
+class FPExceptionDisableDivByZero : public FPControlWord<_MM_MASK_DIV_ZERO, _MM_MASK_DIV_ZERO> { };
+
+#elif defined(JPH_USE_NEON)
+
+/// Invalid operation exception bit
+static constexpr uint64 FP_IOE = 1 << 8;
+
+/// Enable divide by zero exception bit
+static constexpr uint64 FP_DZE = 1 << 9;
+
+/// Enable floating point divide by zero exception and exceptions on invalid numbers
+class FPExceptionsEnable : public FPControlWord<FP_IOE | FP_DZE, FP_IOE | FP_DZE> { };
+
+/// Disable invalid floating point value exceptions
+class FPExceptionDisableInvalid : public FPControlWord<0, FP_IOE> { };
+
+/// Disable division by zero floating point exceptions
+class FPExceptionDisableDivByZero : public FPControlWord<0, FP_DZE> { };
+
+#else
+
+#error Unsupported CPU architecture
+
+#endif
+
+#else
+
+/// Dummy implementations
+class FPExceptionsEnable { };
+class FPExceptionDisableInvalid { };
+class FPExceptionDisableDivByZero { };
+
+#endif
+
+} // JPH

+ 31 - 0
Jolt/Core/FPFlushDenormals.h

@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/FPControlWord.h>
+
+namespace JPH {
+
+#if defined(JPH_USE_SSE)
+
+/// Helper class that needs to be put on the stack to enable flushing denormals to zero
+/// This can make floating point operations much faster when working with very small numbers
+class FPFlushDenormals : public FPControlWord<_MM_FLUSH_ZERO_ON, _MM_FLUSH_ZERO_MASK> { };
+
+#elif defined(JPH_USE_NEON)
+
+/// Flush denormals to zero bit
+static constexpr uint64 FP_FZ = 1 << 24;
+
+/// Helper class that needs to be put on the stack to enable flushing denormals to zero
+/// This can make floating point operations much faster when working with very small numbers
+class FPFlushDenormals : public FPControlWord<FP_FZ, FP_FZ> { };
+
+#else
+
+#error Unsupported CPU architecture
+
+#endif
+
+} // JPH

+ 72 - 0
Jolt/Core/Factory.cpp

@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/Factory.h>
+
+namespace JPH {
+
+Factory Factory::sInstance;
+
+void *Factory::CreateObject(const char *inName)
+{ 
+	const RTTI *ci = Find(inName); 
+	return ci != nullptr? ci->CreateObject() : nullptr; 
+}
+
+const RTTI *Factory::Find(const char *inName)
+{ 
+	ClassNameMap::iterator c = mClassNameMap.find(inName); 
+	return c != mClassNameMap.end()? c->second : nullptr; 
+}
+
+const RTTI *Factory::Find(uint32 inHash)
+{ 
+	ClassHashMap::iterator c = mClassHashMap.find(inHash); 
+	return c != mClassHashMap.end()? c->second : nullptr; 
+}
+
+bool Factory::Register(const RTTI *inRTTI)
+{ 
+	// Check if we already know the type
+	if (Find(inRTTI->GetName()) != nullptr)
+		return true;
+
+	// Insert this class by name
+	mClassNameMap.insert(ClassNameMap::value_type(inRTTI->GetName(), inRTTI));
+
+	// Insert this class by hash
+	if (!mClassHashMap.insert(ClassHashMap::value_type(inRTTI->GetHash(), inRTTI)).second)
+	{
+		JPH_ASSERT(false, "Hash collision registering type!");
+		return false;
+	}
+
+	// Register base classes
+	for (int i = 0; i < inRTTI->GetBaseClassCount(); ++i)
+		if (!Register(inRTTI->GetBaseClass(i)))
+			return false;
+
+	// Register attribute classes
+	for (int i = 0; i < inRTTI->GetAttributeCount(); ++i)
+	{
+		const RTTI *rtti = inRTTI->GetAttribute(i)->GetMemberPrimitiveType();
+		if (rtti != nullptr)
+			if (!Register(rtti))
+				return false;
+	}
+
+	return true;
+}
+
+const vector<const RTTI *> Factory::GetAllClasses()
+{
+	vector<const RTTI *> all_classes;
+	all_classes.reserve(mClassNameMap.size());
+	for (ClassNameMap::iterator c = mClassNameMap.begin(); c != mClassNameMap.end(); ++c)
+		all_classes.push_back(c->second);
+	return all_classes;
+}
+
+} // JPH

+ 45 - 0
Jolt/Core/Factory.h

@@ -0,0 +1,45 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/RTTI.h>
+#include <unordered_map>
+
+namespace JPH {
+
+/// Factory, to create RTTI objects
+class Factory
+{
+public:
+	/// Create an object
+	void *						CreateObject(const char *inName);
+
+	/// Find type info for a specific class by name
+	const RTTI *				Find(const char *inName);
+
+	/// Find type info for a specific class by hash
+	const RTTI *				Find(uint32 inHash);
+
+	/// Register an object with the factory. Returns false on failure.
+	bool						Register(const RTTI *inRTTI);
+
+	/// Get all registered classes
+	const vector<const RTTI *>	GetAllClasses();
+
+	/// Singleton factory instance
+	static Factory 				sInstance;
+
+private:
+	using ClassNameMap = unordered_map<string, const RTTI *>;
+
+	using ClassHashMap = unordered_map<uint32, const RTTI *>;
+
+	/// Map of class names to type info
+	ClassNameMap				mClassNameMap;
+
+	// Map of class hash to type info
+	ClassHashMap				mClassHashMap;
+};
+
+} // JPH

+ 116 - 0
Jolt/Core/FixedSizeFreeList.h

@@ -0,0 +1,116 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/NonCopyable.h>
+#include <Core/Mutex.h>
+#include <Core/Memory.h>
+#include <atomic>
+
+namespace JPH {
+
+/// Class that allows lock free creation / destruction of objects (unless a new page of objects needs to be allocated)
+/// It contains a fixed pool of objects and also allows batching up a lot of objects to be destroyed
+/// and doing the actual free in a single atomic operation
+template <typename Object>
+class FixedSizeFreeList : public NonCopyable
+{
+private:
+	/// Storage type for an Object
+	struct alignas(Object) ObjectStorage
+	{
+		/// Constructor to satisfy the vector class
+							ObjectStorage() { }
+							ObjectStorage(const ObjectStorage &inRHS) : mNextFreeObject(inRHS.mNextFreeObject.load()) { memcpy(mData, inRHS.mData, sizeof(Object)); }
+
+		/// Storage space for the Object, stored as uninitialized data
+		uint8				mData[sizeof(Object)];
+
+		/// When the object is freed (or in the process of being freed as a batch) this will contain the next free object
+		/// When an object is in use it will contain the object's index in the free list
+		atomic<uint32>		mNextFreeObject;
+	};
+
+	static_assert(alignof(ObjectStorage) == alignof(Object), "Object not properly aligned");
+
+	/// Access the object storage given the object index
+	const ObjectStorage &	GetStorage(uint32 inObjectIndex) const	{ return mPages[inObjectIndex / mPageSize][inObjectIndex % mPageSize]; }
+	ObjectStorage &			GetStorage(uint32 inObjectIndex)		{ return mPages[inObjectIndex / mPageSize][inObjectIndex % mPageSize]; }
+
+	/// Number of objects that we currently have in the free list / new pages
+	atomic<uint32>			mNumFreeObjects;
+
+	/// Simple counter that makes the first free object pointer update with every CAS so that we don't suffer from the ABA problem
+	atomic<uint32>			mAllocationTag;
+
+	/// Index of first free object, the first 32 bits of an object are used to point to the next free object
+	atomic<uint64>			mFirstFreeObjectAndTag;
+
+	/// Size (in objects) of a single page
+	uint32					mPageSize;
+
+	/// Total number of pages that are usable
+	uint32					mNumPages;
+
+	/// Total number of objects that have been allocated
+	uint32					mNumObjectsAllocated;
+
+	/// The first free object to use when the free list is empty (may need to allocate a new page)
+	atomic<uint32>			mFirstFreeObjectInNewPage;
+
+	/// Array of pages of objects
+	ObjectStorage **		mPages = nullptr;
+
+	/// Mutex that is used to allocate a new page if the storage runs out
+	Mutex					mPageMutex;
+
+public:
+	/// Invalid index
+	static const uint32		cInvalidObjectIndex = 0xffffffff;
+
+	/// Size of an object + bookkeeping for the freelist
+	static const int		ObjectStorageSize = sizeof(ObjectStorage);
+
+	/// Destructor
+	inline					~FixedSizeFreeList();
+
+	/// Initialize the free list, up to inMaxObjects can be allocated
+	inline void				Init(uint inMaxObjects, uint inPageSize);
+
+	/// Lockless construct a new object, inParameters are passed on to the constructor
+	template <typename... Parameters>
+	inline uint32			ConstructObject(Parameters &&... inParameters);
+
+	/// Lockless destruct an object and return it to the free pool
+	inline void				DestructObject(uint32 inObjectIndex);
+
+	/// Lockless destruct an object and return it to the free pool
+	inline void				DestructObject(Object *inObject);
+
+	/// A batch of objects that can be destructed
+	struct Batch
+	{
+		uint32				mFirstObjectIndex = cInvalidObjectIndex;
+		uint32				mLastObjectIndex = cInvalidObjectIndex;
+		uint32				mNumObjects = 0;
+	};
+
+	/// Add a object to an existing batch to be destructed.
+	/// Adding objects to a batch does not destroy or modify the objects, this will merely link them
+	/// so that the entire batch can be returned to the free list in a single atomic operation
+	inline void				AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex);
+
+	/// Lockless destruct batch of objects
+	inline void				DestructObjectBatch(Batch &ioBatch);
+
+	/// Access an object by index.
+	inline Object &			Get(uint32 inObjectIndex)				{ return reinterpret_cast<Object &>(GetStorage(inObjectIndex).mData); }
+
+	/// Access an object by index.
+	inline const Object &	Get(uint32 inObjectIndex) const			{ return reinterpret_cast<const Object &>(GetStorage(inObjectIndex).mData); }
+};
+
+} // JPH
+
+#include <Core/FixedSizeFreeList.inl>

+ 202 - 0
Jolt/Core/FixedSizeFreeList.inl

@@ -0,0 +1,202 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+namespace JPH {
+
+template <typename Object>
+FixedSizeFreeList<Object>::~FixedSizeFreeList()
+{
+	// Ensure everything is freed before the freelist is destructed
+	JPH_ASSERT(mNumFreeObjects == mNumPages * mPageSize);
+
+	// Free memory for pages
+	uint32 num_pages = mNumObjectsAllocated / mPageSize;
+	for (uint32 page = 0; page < num_pages; ++page)
+		AlignedFree(mPages[page]);
+	delete [] mPages;
+}
+
+template <typename Object>
+void FixedSizeFreeList<Object>::Init(uint inMaxObjects, uint inPageSize)
+{
+	// Check sanity
+	JPH_ASSERT(mPages == nullptr);
+
+	// Store configuration parameters
+	mNumPages = (inMaxObjects + inPageSize - 1) / inPageSize;
+	mNumFreeObjects = mNumPages * inPageSize;
+	mPageSize = inPageSize;
+
+	// Allocate page table
+	mPages = new ObjectStorage * [mNumPages];
+
+	// We didn't yet use any objects of any page
+	mNumObjectsAllocated = 0;
+	mFirstFreeObjectInNewPage = 0;
+
+	// Start with 1 as the first tag
+	mAllocationTag = 1;
+
+	// Set first free object (with tag 0)
+	mFirstFreeObjectAndTag = cInvalidObjectIndex;
+}
+
+template <typename Object>
+template <typename... Parameters>
+uint32 FixedSizeFreeList<Object>::ConstructObject(Parameters &&... inParameters)
+{
+	for (;;)
+	{
+		// Get first object from the linked list
+		uint64 first_free_object_and_tag = mFirstFreeObjectAndTag;
+		uint32 first_free = uint32(first_free_object_and_tag);
+		if (first_free == cInvalidObjectIndex)
+		{
+			// The free list is empty, we take an object from the page that has never been used before
+			first_free = mFirstFreeObjectInNewPage++;
+			if (first_free >= mNumObjectsAllocated)
+			{
+				// Allocate new page
+				lock_guard<Mutex> lock(mPageMutex);
+				while (first_free >= mNumObjectsAllocated)
+				{
+					uint32 next_page = mNumObjectsAllocated / mPageSize;
+					if (next_page == mNumPages)
+						return cInvalidObjectIndex; // Out of space!
+					mPages[next_page] = reinterpret_cast<ObjectStorage *>(AlignedAlloc(mPageSize * sizeof(ObjectStorage), JPH_CACHE_LINE_SIZE));
+					mNumObjectsAllocated += mPageSize;
+				}
+			}
+
+			// Allocation successful
+			--mNumFreeObjects;
+			ObjectStorage &storage = GetStorage(first_free);
+			new (&storage.mData) Object(forward<Parameters>(inParameters)...);
+			storage.mNextFreeObject = first_free;
+			return first_free;
+		}
+		else
+		{
+			// Load next pointer
+			uint32 new_first_free = GetStorage(first_free).mNextFreeObject;
+
+			// Construct a new first free object tag
+			uint64 new_first_free_object_and_tag = uint64(new_first_free) + (uint64(mAllocationTag++) << 32);
+
+			// Compare and swap
+			if (mFirstFreeObjectAndTag.compare_exchange_strong(first_free_object_and_tag, new_first_free_object_and_tag))
+			{
+				// Allocation successful
+				--mNumFreeObjects;
+				ObjectStorage &storage = GetStorage(first_free);
+				new (&storage.mData) Object(forward<Parameters>(inParameters)...);
+				storage.mNextFreeObject = first_free;
+				return first_free;
+			}
+		}
+	}
+}
+
+template <typename Object>
+void FixedSizeFreeList<Object>::AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex)
+{
+	JPH_ASSERT(GetStorage(inObjectIndex).mNextFreeObject == inObjectIndex, "Trying to add a object to the batch that is already in a free list");
+	JPH_ASSERT(ioBatch.mNumObjects != uint32(-1), "Trying to reuse a batch that has already been freed");
+
+	// Link object in batch to free
+	if (ioBatch.mFirstObjectIndex == cInvalidObjectIndex)
+		ioBatch.mFirstObjectIndex = inObjectIndex;
+	else
+		GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject = inObjectIndex;
+	ioBatch.mLastObjectIndex = inObjectIndex;
+	ioBatch.mNumObjects++;
+}
+
+template <typename Object>
+void FixedSizeFreeList<Object>::DestructObjectBatch(Batch &ioBatch)
+{
+	if (ioBatch.mFirstObjectIndex != cInvalidObjectIndex)
+	{
+		// Call destructors
+		if (!is_trivially_destructible<Object>())
+		{
+			uint32 object_idx = ioBatch.mFirstObjectIndex;
+			do
+			{
+				ObjectStorage &storage = GetStorage(object_idx);
+				reinterpret_cast<Object &>(storage.mData).~Object();
+				object_idx = storage.mNextFreeObject;
+			}
+			while (object_idx != cInvalidObjectIndex);
+		}
+
+		// Add to objects free list
+		for (;;)
+		{
+			// Get first object from the list
+			uint64 first_free_object_and_tag = mFirstFreeObjectAndTag;
+			uint32 first_free = uint32(first_free_object_and_tag);
+
+			// Make it the next pointer of the last object in the batch that is to be freed
+			GetStorage(ioBatch.mLastObjectIndex).mNextFreeObject = first_free;
+
+			// Construct a new first free object tag
+			uint64 new_first_free_object_and_tag = uint64(ioBatch.mFirstObjectIndex) + (uint64(mAllocationTag++) << 32);
+
+			// Compare and swap
+			if (mFirstFreeObjectAndTag.compare_exchange_strong(first_free_object_and_tag, new_first_free_object_and_tag))
+			{
+				// Free successful
+				mNumFreeObjects += ioBatch.mNumObjects;
+
+				// Mark the batch as freed
+#ifdef JPH_ENABLE_ASSERTS
+				ioBatch.mNumObjects = uint32(-1);
+#endif		
+				return;
+			}
+		}
+	}
+}
+
+template <typename Object>
+void FixedSizeFreeList<Object>::DestructObject(uint32 inObjectIndex)
+{
+	JPH_ASSERT(inObjectIndex != cInvalidObjectIndex);
+
+	// Call destructor
+	ObjectStorage &storage = GetStorage(inObjectIndex); 
+	reinterpret_cast<Object &>(storage.mData).~Object();
+
+	// Add to object free list
+	for (;;)
+	{
+		// Get first object from the list
+		uint64 first_free_object_and_tag = mFirstFreeObjectAndTag;
+		uint32 first_free = uint32(first_free_object_and_tag);
+
+		// Make it the next pointer of the last object in the batch that is to be freed
+		storage.mNextFreeObject = first_free;
+
+		// Construct a new first free object tag
+		uint64 new_first_free_object_and_tag = uint64(inObjectIndex) + (uint64(mAllocationTag++) << 32);
+
+		// Compare and swap
+		if (mFirstFreeObjectAndTag.compare_exchange_strong(first_free_object_and_tag, new_first_free_object_and_tag))
+		{
+			// Free successful
+			mNumFreeObjects++;
+			return;
+		}
+	}
+}
+
+template<typename Object>
+inline void FixedSizeFreeList<Object>::DestructObject(Object *inObject)
+{
+	uint32 index = reinterpret_cast<ObjectStorage *>(inObject)->mNextFreeObject;
+	JPH_ASSERT(index < mNumObjectsAllocated);
+	DestructObject(index);
+}
+
+} // JPH

+ 51 - 0
Jolt/Core/HashCombine.h

@@ -0,0 +1,51 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+inline void hash_combine(std::size_t &ioSeed) 
+{ 
+}
+
+/// Hash combiner to use a custom struct in an unordered map or set
+/// Taken from: https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x
+///
+/// Usage:
+///
+///		struct SomeHashKey 
+///		{
+///		    std::string key1;
+///		    std::string key2;
+///		    bool key3;
+///		};
+/// 
+///		JPH_MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3)
+template <typename T, typename... Rest>
+inline void hash_combine(std::size_t &ioSeed, const T &inValue, Rest... inRest) 
+{
+	std::hash<T> hasher;
+    ioSeed ^= hasher(inValue) + 0x9e3779b9 + (ioSeed << 6) + (ioSeed >> 2);
+    hash_combine(ioSeed, inRest...);
+}
+
+} // JPH
+
+#define JPH_MAKE_HASH_STRUCT(type, name, ...)				\
+	struct [[nodiscard]] name								\
+	{														\
+        std::size_t operator()(const type &t) const			\
+		{													\
+            std::size_t ret = 0;							\
+            ::JPH::hash_combine(ret, __VA_ARGS__);			\
+            return ret;										\
+        }													\
+    };
+
+#define JPH_MAKE_HASHABLE(type, ...)						\
+    namespace std											\
+	{														\
+        template<>											\
+		JPH_MAKE_HASH_STRUCT(type, hash<type>, __VA_ARGS__)	\
+    }

+ 27 - 0
Jolt/Core/IssueReporting.cpp

@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+#include <fstream>
+
+namespace JPH {
+
+static void DummyTrace(const char *inFMT, ...) 
+{ 
+	JPH_ASSERT(false); 
+};
+
+TraceFunction Trace = DummyTrace;
+
+#ifdef JPH_ENABLE_ASSERTS
+
+static bool DummyAssertFailed(const char *inExpression, const char *inMessage, const char *inFile, uint inLine)
+{ 
+	return true; // Trigger breakpoint
+};
+
+AssertFailedFunction AssertFailed = DummyAssertFailed;
+
+#endif // JPH_ENABLE_ASSERTS
+
+} // JPH

+ 37 - 0
Jolt/Core/IssueReporting.h

@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+	
+/// Trace function, needs to be overridden by application. This should output a line of text to the log / TTY.
+using TraceFunction = void (*)(const char *inFMT, ...);
+extern TraceFunction Trace;
+
+// Always turn on asserts in Debug mode
+#if defined(_DEBUG) && !defined(JPH_ENABLE_ASSERTS)
+	#define JPH_ENABLE_ASSERTS
+#endif
+
+#ifdef JPH_ENABLE_ASSERTS
+	/// Function called when an assertion fails. This function should return true if a breakpoint needs to be triggered
+	using AssertFailedFunction = bool(*)(const char *inExpression, const char *inMessage, const char *inFile, uint inLine);
+	extern AssertFailedFunction AssertFailed;
+
+	// Helper functions to pass message on to failed function
+	struct AssertLastParam { };
+	inline bool AssertFailedParamHelper(const char *inExpression, const char *inFile, uint inLine, AssertLastParam) { return AssertFailed(inExpression, nullptr, inFile, inLine); }
+	inline bool AssertFailedParamHelper(const char *inExpression, const char *inFile, uint inLine, const char *inMessage, AssertLastParam) { return AssertFailed(inExpression, inMessage, inFile, inLine); }
+
+	/// Main assert macro, usage: JPH_ASSERT(condition, message) or JPH_ASSERT(condition)
+	#define JPH_ASSERT(inExpression, ...)	do { if (!(inExpression) && AssertFailedParamHelper(#inExpression, __FILE__, uint(__LINE__), ##__VA_ARGS__, AssertLastParam())) JPH_BREAKPOINT; } while (false)
+
+	#define JPH_IF_ENABLE_ASSERTS(...)		__VA_ARGS__
+#else
+    #define JPH_ASSERT(...)					((void)0)
+
+	#define JPH_IF_ENABLE_ASSERTS(...)	
+#endif // JPH_ENABLE_ASSERTS
+
+} // JPH

+ 255 - 0
Jolt/Core/JobSystem.h

@@ -0,0 +1,255 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/Reference.h>
+#include <Core/Color.h>
+#include <Core/Profiler.h>
+#include <Core/NonCopyable.h>
+#include <Core/StaticArray.h>
+#include <atomic>
+
+namespace JPH {
+
+/// A class that allows units of work (Jobs) to be scheduled across multiple threads.
+/// It allows dependencies between the jobs so that the jobs form a graph.
+/// 
+/// The pattern for using this class is:
+/// 
+///		// Create job system
+///		JobSystem *job_system = new JobSystemThreadPool(...);
+///		
+///		// Create some jobs
+///		JobHandle second_job = job_system->CreateJob("SecondJob", Color::sRed, []() { ... }, 1); // Create a job with 1 dependency
+///		JobHandle first_job = job_system->CreateJob("FirstJob", Color::sGreen, [second_job]() { ....; second_job.RemoveDependency(); }, 0); // Job can start immediately, will start second job when it's done
+///		JobHandle third_job = job_system->CreateJob("ThirdJob", Color::sBlue, []() { ... }, 0); // This job can run immediately as well and can run in parallel to job 1 and 2
+///		
+///		// Add the jobs to the barrier so that we can execute them while we're waiting
+///		Barrier *barrier = job_system->CreateBarrier();
+///		barrier->AddJob(first_job);
+///		barrier->AddJob(second_job);
+///		barrier->AddJob(third_job);
+///		job_system->WaitForJobs(barrier);
+///		
+/// 	// Clean up
+/// 	job_system->DestroyBarrier(barrier);
+/// 	delete job_system;
+///	
+///	Jobs are guaranteed to be started in the order that their dependency counter becomes zero (in case they're scheduled on a background thread) 
+///	or in the order they're added to the barrier (when dependency count is zero and when executing on the thread that calls WaitForJobs).
+class JobSystem : public NonCopyable
+{
+protected:
+	class Job;
+
+public:
+	/// A job handle contains a reference to a job. The job will be deleted as soon as there are no JobHandles.
+	/// referring to the job and when it is not in the job queue / being processed.
+	class JobHandle : private Ref<Job>
+	{
+	public:
+		/// Constructor 
+		inline				JobHandle()									{ }
+		inline				JobHandle(const JobHandle &inHandle)		: Ref<Job>(inHandle) { }
+		inline				JobHandle(JobHandle &&inHandle)				: Ref<Job>(move(inHandle)) { }
+
+		/// Constructor, only to be used by JobSystem
+		inline				JobHandle(Job *inJob)						: Ref<Job>(inJob) { }
+
+		/// Assignment
+		inline JobHandle &	operator = (const JobHandle &inHandle)		{ Ref<Job>::operator = (inHandle); return *this; }
+		inline JobHandle &	operator = (JobHandle &&inHandle)			{ Ref<Job>::operator = (move(inHandle)); return *this; }
+
+		/// Check if this handle contains a job
+		inline bool			IsValid() const								{ return GetPtr() != nullptr; }
+
+		/// Check if this job has finished executing
+		inline bool			IsDone() const								{ return GetPtr() != nullptr && GetPtr()->IsDone(); }
+
+		/// Add to the dependency counter. 
+		inline void			AddDependency(int inCount = 1) const		{ GetPtr()->AddDependency(inCount); }
+
+		/// Remove from the dependency counter. Job will start whenever the dependency counter reaches zero
+		/// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions.
+		inline void			RemoveDependency(int inCount = 1) const		{ GetPtr()->RemoveDependencyAndQueue(inCount); }
+
+		/// Remove a dependency from a batch of jobs at once, this can be more efficient than removing them one by one as it requires less locking
+		static inline void	sRemoveDependencies(JobHandle *inHandles, uint inNumHandles, int inCount = 1);
+
+		/// Helper function to remove dependencies on a static array of job handles
+		template <uint N>
+		static inline void	sRemoveDependencies(StaticArray<JobHandle, N> &inHandles, int inCount = 1)
+		{
+			sRemoveDependencies(inHandles.data(), inHandles.size(), inCount);
+		}
+
+		/// Inherit the GetPtr function, only to be used by the JobSystem
+		using Ref<Job>::GetPtr;
+	};
+
+	/// A job barrier keeps track of a number of jobs and allows waiting until they are all completed.
+	class Barrier : public NonCopyable
+	{
+	public:
+		/// Add a job to this barrier
+		/// Note that jobs can keep being added to the barrier while waiting for the barrier
+		virtual void		AddJob(const JobHandle &inJob) = 0;
+
+		/// Add multiple jobs to this barrier
+		/// Note that jobs can keep being added to the barrier while waiting for the barrier
+		virtual void		AddJobs(const JobHandle *inHandles, uint inNumHandles) = 0;
+
+	protected:
+		/// Job needs to be able to call OnJobFinished
+		friend class Job;
+
+		/// Destructor, you should call JobSystem::DestroyBarrier instead of destructing this object directly
+		virtual				~Barrier()									{ }
+
+		/// Called by a Job to mark that it is finished
+		virtual void		OnJobFinished(Job *inJob) = 0;
+	};
+
+	/// Main function of the job
+	using JobFunction = function<void()>;
+
+	/// Destructor
+	virtual					~JobSystem()								{ }
+
+	/// Get maximum number of concurrently executing jobs
+	virtual int				GetMaxConcurrency() const = 0;
+
+	/// Create a new job, the job is started immediately if inNumDependencies == 0 otherwise it starts when
+	/// RemoveDependency causes the dependency counter to reach 0.
+	virtual JobHandle		CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) = 0;
+
+	/// Create a new barrier, used to wait on jobs
+	virtual Barrier *		CreateBarrier() = 0;
+
+	/// Destroy a barrier when it is no longer used. The barrier should be empty at this point.
+	virtual void			DestroyBarrier(Barrier *inBarrier) = 0;
+
+	/// Wait for a set of jobs to be finished, note that only 1 thread can be waiting on a barrier at a time
+	virtual void			WaitForJobs(Barrier *inBarrier) = 0;
+
+protected:
+	/// A class that contains information for a single unit of work
+	class Job
+	{
+	public:
+		/// Constructor
+							Job(const char *inJobName, ColorArg inColor, JobSystem *inJobSystem, const JobFunction &inJobFunction, uint32 inNumDependencies) : 
+		#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+			mJobName(inJobName), 
+			mColor(inColor), 
+		#endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+			mJobSystem(inJobSystem), 
+			mJobFunction(inJobFunction), 
+			mReferenceCount(0), 
+			mNumDependencies(inNumDependencies) 
+		{ 
+		}
+
+		/// Get the jobs system to which this job belongs
+		inline JobSystem *	GetJobSystem()								{ return mJobSystem; }
+
+		/// Add or release a reference to this object
+		inline void			AddRef() 									{ ++mReferenceCount; }
+		inline void			Release()									{ if (--mReferenceCount == 0) mJobSystem->FreeJob(this); }
+
+		/// Add to the dependency counter. 
+		inline void			AddDependency(int inCount);
+
+		/// Remove from the dependency counter. Returns true whenever the dependency counter reaches zero
+		/// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions.
+		inline bool			RemoveDependency(int inCount);
+
+		/// Remove from the dependency counter. Job will be queued whenever the dependency counter reaches zero
+		/// and if it does it is no longer valid to call the AddDependency/RemoveDependency functions.
+		inline void			RemoveDependencyAndQueue(int inCount);
+
+		/// Set the job barrier that this job belongs to and returns false if this was not possible because the job already finished
+		inline bool			SetBarrier(Barrier *inBarrier)
+		{ 
+			intptr_t barrier = 0; 
+			if (mBarrier.compare_exchange_strong(barrier, reinterpret_cast<intptr_t>(inBarrier)))
+				return true; 
+			JPH_ASSERT(barrier == cBarrierDoneState, "A job can only belong to 1 barrier");
+			return false;
+		}
+
+		/// Run the job function, returns the number of dependencies that this job still has or cExecutingState or cDoneState
+		inline uint32		Execute()
+		{
+			// Transition job to executing state
+			uint32 state = 0; // We can only start running with a dependency counter of 0
+			if (!mNumDependencies.compare_exchange_strong(state, cExecutingState))
+				return state; // state is updated by compare_exchange_strong to the current value
+
+			// Run the job function
+			{
+				JPH_PROFILE(mJobName, mColor.GetUInt32());
+				mJobFunction();
+			}
+
+			// Fetch the barrier pointer and exchange it for the done state, so we're sure that no barrier gets set after we want to call the callback
+			intptr_t barrier;
+			for (;;)
+			{
+				barrier = mBarrier;
+				if (mBarrier.compare_exchange_strong(barrier, cBarrierDoneState))
+					break;
+			}
+			JPH_ASSERT(barrier != cBarrierDoneState);
+
+			// Mark job as done
+			state = cExecutingState;
+			mNumDependencies.compare_exchange_strong(state, cDoneState);
+			JPH_ASSERT(state == cExecutingState);
+
+			// Notify the barrier after we've changed the job to the done state so that any thread reading the state after receiving the callback will see that the job has finished
+			if (barrier != 0)
+				reinterpret_cast<Barrier *>(barrier)->OnJobFinished(this);
+
+			return cDoneState;
+		}
+
+		/// Test if the job can be executed
+		inline bool			CanBeExecuted() const						{ return mNumDependencies == 0; }
+
+		/// Test if the job finished executing
+		inline bool			IsDone() const								{ return mNumDependencies == cDoneState; }
+
+		static constexpr uint32 cExecutingState = 0xe0e0e0e0;			///< Value of mNumDependencies when job is executing
+		static constexpr uint32 cDoneState		= 0xd0d0d0d0;			///< Value of mNumDependencies when job is done executing
+
+		static constexpr intptr_t cBarrierDoneState = ~intptr_t(0);		///< Value to use when the barrier has been triggered
+
+private:
+	#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+		const char *		mJobName;									///< Name of the job
+		Color				mColor;										///< Color of the job in the profiler
+	#endif // defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+		JobSystem *			mJobSystem;									///< The job system we belong to
+		atomic<intptr_t>	mBarrier = 0;								///< Barrier that this job is associated with (is a Barrier pointer)
+		JobFunction			mJobFunction;								///< Main job function
+		atomic<uint32>		mReferenceCount;							///< Amount of JobHandles pointing to this job
+		atomic<uint32>		mNumDependencies;							///< Amount of jobs that need to complete before this job can run
+	};
+
+	/// Adds a job to the job queue
+	virtual void			QueueJob(Job *inJob) = 0;
+
+	/// Adds a number of jobs at once to the job queue
+	virtual void			QueueJobs(Job **inJobs, uint inNumJobs) = 0;
+
+	/// Frees a job
+	virtual void			FreeJob(Job *inJob) = 0;
+};
+
+using JobHandle = JobSystem::JobHandle;
+
+} // JPH
+
+#include <Core/JobSystem.inl>

+ 55 - 0
Jolt/Core/JobSystem.inl

@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+namespace JPH {
+
+void JobSystem::Job::AddDependency(int inCount)
+{
+	JPH_IF_ENABLE_ASSERTS(uint32 old_value =) mNumDependencies.fetch_add(inCount);
+	JPH_ASSERT(old_value > 0 && old_value != cExecutingState && old_value != cDoneState, "Job is queued, running or done, it is not allowed to add a dependency to a running job");
+}
+
+bool JobSystem::Job::RemoveDependency(int inCount)
+{
+	uint32 old_value = mNumDependencies.fetch_sub(inCount);
+	JPH_ASSERT(old_value != cExecutingState && old_value != cDoneState, "Job is running or done, it is not allowed to add a dependency to a running job");
+	uint32 new_value = old_value - inCount;
+	JPH_ASSERT(old_value > new_value, "Test wrap around, this is a logic error");
+	return new_value == 0;
+}
+
+void JobSystem::Job::RemoveDependencyAndQueue(int inCount)
+{
+	if (RemoveDependency(inCount))
+		mJobSystem->QueueJob(this);
+}
+
+void JobSystem::JobHandle::sRemoveDependencies(JobHandle *inHandles, uint inNumHandles, int inCount)
+{
+	JPH_PROFILE_FUNCTION();
+
+	JPH_ASSERT(inNumHandles > 0);
+
+	// Get the job system, all jobs should be part of the same job system
+	JobSystem *job_system = inHandles->GetPtr()->GetJobSystem();
+
+	// Allocate a buffer to store the jobs that need to be queued
+	Job **jobs_to_queue = (Job **)JPH_STACK_ALLOC(inNumHandles * sizeof(Job *));
+	Job **next_job = jobs_to_queue;
+
+	// Remove the dependencies on all jobs
+	for (JobHandle *handle = inHandles, *handle_end = inHandles + inNumHandles; handle < handle_end; ++handle)
+	{
+		Job *job = handle->GetPtr();
+		JPH_ASSERT(job->GetJobSystem() == job_system); // All jobs should belong to the same job system
+		if (job->RemoveDependency(inCount))
+			*(next_job++) = job;
+	}
+
+	// If any jobs need to be scheduled, schedule them as a batch
+	uint num_jobs_to_queue = uint(next_job - jobs_to_queue);
+	if (num_jobs_to_queue != 0)
+		job_system->QueueJobs(jobs_to_queue, num_jobs_to_queue);
+}
+
+} // JPH

+ 570 - 0
Jolt/Core/JobSystemThreadPool.cpp

@@ -0,0 +1,570 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/JobSystemThreadPool.h>
+#include <Core/Profiler.h>
+#include <Core/FPException.h>
+#include <algorithm>
+
+#ifdef JPH_PLATFORM_WINDOWS
+	#pragma warning (push, 0)
+	#pragma warning (disable : 5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception.
+	#define WIN32_LEAN_AND_MEAN
+	#include <windows.h>
+	#pragma warning (pop)
+#endif
+
+namespace JPH {
+
+JobSystemThreadPool::Semaphore::Semaphore()
+{
+#ifdef JPH_PLATFORM_WINDOWS
+	mSemaphore = CreateSemaphore(nullptr, 0, INT_MAX, nullptr);
+#endif
+}
+
+JobSystemThreadPool::Semaphore::~Semaphore()
+{
+#ifdef JPH_PLATFORM_WINDOWS
+	CloseHandle(mSemaphore);
+#endif
+}
+
+void JobSystemThreadPool::Semaphore::Release(uint inNumber)
+{
+	JPH_ASSERT(inNumber > 0);
+
+#ifdef JPH_PLATFORM_WINDOWS
+	int old_value = mCount.fetch_add(inNumber);
+	if (old_value < 0)
+	{
+		int new_value = old_value + (int)inNumber;
+		int num_to_release = min(new_value, 0) - old_value;
+		::ReleaseSemaphore(mSemaphore, num_to_release, nullptr);
+	}
+#else
+	lock_guard<mutex> lock(mLock);
+	mCount += (int)inNumber;
+	if (inNumber > 1)
+		mWaitVariable.notify_all();
+	else
+		mWaitVariable.notify_one();
+#endif
+}
+
+void JobSystemThreadPool::Semaphore::Acquire(uint inNumber)
+{
+	JPH_ASSERT(inNumber > 0);
+
+#ifdef JPH_PLATFORM_WINDOWS
+	int old_value = mCount.fetch_sub(inNumber);
+	int new_value = old_value - (int)inNumber;
+	if (new_value < 0)
+	{
+		int num_to_acquire = min(old_value, 0) - new_value;
+		for (int i = 0; i < num_to_acquire; ++i)
+			WaitForSingleObject(mSemaphore, INFINITE);
+	}
+#else
+	unique_lock<mutex> lock(mLock);
+	mCount -= (int)inNumber;
+	while (mCount < 0)
+		mWaitVariable.wait(lock);
+#endif
+}
+
+JobSystemThreadPool::BarrierImpl::BarrierImpl()
+{
+	for (atomic<Job *> &j : mJobs)
+		j = nullptr;
+}
+
+JobSystemThreadPool::BarrierImpl::~BarrierImpl()
+{
+	JPH_ASSERT(IsEmpty());
+}
+
+void JobSystemThreadPool::BarrierImpl::AddJob(const JobHandle &inJob)
+{
+	JPH_PROFILE_FUNCTION();
+
+	bool release_semaphore = false;
+
+	// Set the barrier on the job, this returns true if the barrier was successfully set (otherwise the job is already done and we don't need to add it to our list)
+	Job *job = inJob.GetPtr();
+	if (job->SetBarrier(this))
+	{
+		// If the job can be executed we want to release the semaphore an extra time to allow the waiting thread to start executing it
+		mNumToAcquire++;
+		if (job->CanBeExecuted())
+		{
+			release_semaphore = true;
+			mNumToAcquire++;
+		}
+
+		// Add the job to our job list
+		job->AddRef();
+		uint write_index = mJobWriteIndex++;
+		while (write_index - mJobReadIndex >= cMaxJobs)
+		{
+			JPH_ASSERT(false, "Barrier full, stalling!");
+			this_thread::sleep_for(100us);
+		}
+		mJobs[write_index & (cMaxJobs - 1)] = job;
+	}
+
+	// Notify waiting thread that a new executable job is available
+	if (release_semaphore)
+		mSemaphore.Release();
+}
+
+void JobSystemThreadPool::BarrierImpl::AddJobs(const JobHandle *inHandles, uint inNumHandles)
+{
+	JPH_PROFILE_FUNCTION();
+
+	bool release_semaphore = false;
+
+	for (const JobHandle *handle = inHandles, *handles_end = inHandles + inNumHandles; handle < handles_end; ++handle)
+	{
+		// Set the barrier on the job, this returns true if the barrier was successfully set (otherwise the job is already done and we don't need to add it to our list)
+		Job *job = handle->GetPtr();
+		if (job->SetBarrier(this))
+		{
+			// If the job can be executed we want to release the semaphore an extra time to allow the waiting thread to start executing it
+			mNumToAcquire++;
+			if (!release_semaphore && job->CanBeExecuted())
+			{
+				release_semaphore = true;
+				mNumToAcquire++;
+			}
+
+			// Add the job to our job list
+			job->AddRef();
+			uint write_index = mJobWriteIndex++;
+			while (write_index - mJobReadIndex >= cMaxJobs)
+			{
+				JPH_ASSERT(false, "Barrier full, stalling!");
+				this_thread::sleep_for(100us);
+			}
+			mJobs[write_index & (cMaxJobs - 1)] = job;
+		}
+	}
+
+	// Notify waiting thread that a new executable job is available
+	if (release_semaphore)
+		mSemaphore.Release();
+}
+
+void JobSystemThreadPool::BarrierImpl::OnJobFinished(Job *inJob)
+{
+	JPH_PROFILE_FUNCTION();
+
+	mSemaphore.Release();
+}
+
+void JobSystemThreadPool::BarrierImpl::Wait()
+{
+	while (mNumToAcquire > 0)
+	{
+		{
+			JPH_PROFILE("Execute Jobs");
+
+			// Go through all jobs
+			bool has_executed;
+			do
+			{
+				has_executed = false;
+
+				// Loop through the jobs and erase jobs from the beginning of the list that are done
+				while (mJobReadIndex < mJobWriteIndex)
+				{				
+					atomic<Job *> &job = mJobs[mJobReadIndex & (cMaxJobs - 1)];
+					Job *job_ptr = job.load();
+					if (job_ptr == nullptr || !job_ptr->IsDone())
+						break;
+
+					// Job is finished, release it
+					job_ptr->Release();
+					job = nullptr;
+					++mJobReadIndex;
+				}
+
+				// Loop through the jobs and execute the first executable job
+				for (uint index = mJobReadIndex; index < mJobWriteIndex; ++index)
+				{
+					atomic<Job *> &job = mJobs[index & (cMaxJobs - 1)];
+					Job *job_ptr = job.load();
+					if (job_ptr != nullptr && job_ptr->CanBeExecuted())
+					{
+						// This will only execute the job if it has not already executed
+						job_ptr->Execute();
+						has_executed = true;
+						break;
+					}
+				}
+
+			} while (has_executed);
+		}
+
+		// Wait for another thread to wake us when either there is more work to do or when all jobs have completed
+		int num_to_acquire = max(1, mSemaphore.GetValue()); // When there have been multiple releases, we acquire them all at the same time to avoid needlessly spinning on executing jobs
+		mSemaphore.Acquire(num_to_acquire);
+		mNumToAcquire -= num_to_acquire;
+	}
+
+	// All jobs should be done now, release them
+	while (mJobReadIndex < mJobWriteIndex)
+	{				
+		atomic<Job *> &job = mJobs[mJobReadIndex & (cMaxJobs - 1)];
+		Job *job_ptr = job.load();
+		JPH_ASSERT(job_ptr != nullptr && job_ptr->IsDone());
+		job_ptr->Release();
+		job = nullptr;
+		++mJobReadIndex;
+	}
+}
+
+JobSystemThreadPool::JobSystemThreadPool(uint inMaxJobs, uint inMaxBarriers, int inNumThreads)	
+{
+	// Init freelist of jobs
+	mJobs.Init(inMaxJobs, inMaxJobs);
+
+	// Init freelist of barriers
+	mMaxBarriers = inMaxBarriers;
+	mBarriers = new BarrierImpl [inMaxBarriers];
+
+	// Init queue
+	for (atomic<Job *> &j : mQueue)
+		j = nullptr;
+
+	// Start the worker threads
+	StartThreads(inNumThreads);
+}
+
+void JobSystemThreadPool::StartThreads(int inNumThreads)
+{
+	// Auto detect number of threads
+	if (inNumThreads < 0)
+		inNumThreads = thread::hardware_concurrency() - 1;
+
+	// If no threads are requested we're done
+	if (inNumThreads == 0)
+		return;
+
+	// Don't quit the threads
+	mQuit = false;
+
+	// Allocate heads
+	mHeads = new atomic<uint> [inNumThreads];
+	for (int i = 0; i < inNumThreads; ++i)
+		mHeads[i] = 0;
+
+	// Start running threads
+	JPH_ASSERT(mThreads.empty());
+	mThreads.reserve(inNumThreads);
+	for (int i = 0; i < inNumThreads; ++i)
+	{
+		// Name the thread
+		stringstream namestream;
+		namestream << "Worker ";
+		namestream << (i + 1);
+		string name = namestream.str();
+
+		// Create thread
+		mThreads.emplace_back([this, name, i] { ThreadMain(name, i); });
+	}
+}
+
+JobSystemThreadPool::~JobSystemThreadPool()
+{
+	// Stop all worker threads
+	StopThreads();
+
+	// Ensure that none of the barriers are used
+#ifdef JPH_ENABLE_ASSERTS
+	for (const BarrierImpl *b = mBarriers, *b_end = mBarriers + mMaxBarriers; b < b_end; ++b)
+		JPH_ASSERT(!b->mInUse);
+#endif // JPH_ENABLE_ASSERTS
+	delete [] mBarriers;
+}
+
+void JobSystemThreadPool::StopThreads()
+{
+	if (mThreads.empty())
+		return;
+
+	// Signal threads that we want to stop and wake them up
+	mQuit = true;
+	mSemaphore.Release((uint)mThreads.size());
+
+	// Wait for all threads to finish
+	for (thread &t : mThreads)
+		if (t.joinable())
+			t.join();
+
+	// Delete all threads
+	mThreads.clear();
+
+	// Ensure that there are no lingering jobs in the queue
+	for (uint head = 0; head != mTail; ++head)
+	{
+		// Fetch job
+		Job *job_ptr = mQueue[head & (cQueueLength - 1)].exchange(nullptr);
+		if (job_ptr != nullptr)
+		{
+			// And execute it
+			job_ptr->Execute();
+			job_ptr->Release();
+		}
+	}
+
+	// Destroy heads and reset tail
+	delete [] mHeads;
+	mHeads = nullptr;
+	mTail = 0;
+}
+
+JobHandle JobSystemThreadPool::CreateJob(const char *inJobName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies)
+{
+	JPH_PROFILE_FUNCTION();
+
+	// Loop until we can get a job from the free list
+	uint32 index;
+	for (;;)
+	{
+		index = mJobs.ConstructObject(inJobName, inColor, this, inJobFunction, inNumDependencies);
+		if (index != AvailableJobs::cInvalidObjectIndex)
+			break;
+		JPH_ASSERT(false, "No jobs available!");
+		this_thread::sleep_for(100us);
+	}
+	Job *job = &mJobs.Get(index);
+	
+	// Construct handle to keep a reference, the job is queued below and may immediately complete
+	JobHandle handle(job);
+	
+	// If there are no dependencies, queue the job now
+	if (inNumDependencies == 0)
+		QueueJob(job);
+
+	// Return the handle
+	return handle;
+}
+
+void JobSystemThreadPool::FreeJob(Job *inJob)
+{
+	mJobs.DestructObject(inJob);
+}
+
+JobSystem::Barrier *JobSystemThreadPool::CreateBarrier()
+{
+	JPH_PROFILE_FUNCTION();
+
+	// Find the first unused barrier
+	for (uint32 index = 0; index < mMaxBarriers; ++index)
+	{
+		bool expected = false;
+		if (mBarriers[index].mInUse.compare_exchange_strong(expected, true))
+			return &mBarriers[index];
+	}
+
+	return nullptr;
+}
+
+void JobSystemThreadPool::DestroyBarrier(Barrier *inBarrier)
+{
+	JPH_PROFILE_FUNCTION();
+
+	// Check that no jobs are in the barrier
+	JPH_ASSERT(static_cast<BarrierImpl *>(inBarrier)->IsEmpty());
+
+	// Flag the barrier as unused
+	bool expected = true;
+	static_cast<BarrierImpl *>(inBarrier)->mInUse.compare_exchange_strong(expected, false);
+	JPH_ASSERT(expected);
+}
+
+void JobSystemThreadPool::WaitForJobs(Barrier *inBarrier)
+{
+	JPH_PROFILE_FUNCTION();
+
+	// Let our barrier implementation wait for the jobs
+	static_cast<BarrierImpl *>(inBarrier)->Wait();
+}
+
+uint JobSystemThreadPool::GetHead() const
+{
+	// Find the minimal value across all threads
+	uint head = mTail;
+	for (size_t i = 0; i < mThreads.size(); ++i)
+		head = min(head, mHeads[i].load());
+	return head;
+}
+
+void JobSystemThreadPool::QueueJobInternal(Job *inJob)
+{
+	// Add reference to job because we're adding the job to the queue
+	inJob->AddRef();
+
+	// Need to read head first because otherwise the tail can already have passed the head
+	// We read the head outside of the loop since it involves iterating over all threads and we only need to update
+	// it if there's not enough space in the queue.
+	uint head = GetHead();
+
+	// Keep track of how many times we slept
+	JPH_IF_ENABLE_ASSERTS(int sleep_count = 0;)
+
+	for (;;)
+	{
+		// Check if there's space in the queue
+		uint old_value = mTail;
+		if (old_value - head >= cQueueLength)
+		{
+			// We calculated the head outside of the loop, update head (and we also need to update tail to prevent it from passing head)
+			head = GetHead();
+			old_value = mTail;
+	
+			// Second check if there's space in the queue
+			if (old_value - head >= cQueueLength)
+			{
+				// If we keep sleeping, something's wrong.
+				JPH_ASSERT(sleep_count++ < 10, "Queue is not being processed quickly enough / too small!");
+
+				// Wake up all threads in order to ensure that they can clear any nullptrs they may not have processed yet
+				mSemaphore.Release((uint)mThreads.size()); 
+
+				// Sleep a little (we have to wait for other threads to update their head pointer in order for us to be able to continue)
+				this_thread::sleep_for(100us);
+				continue;
+			}
+		}
+
+		// Write the job pointer if the slot is empty
+		Job *expected_job = nullptr;
+		bool success = mQueue[old_value & (cQueueLength - 1)].compare_exchange_strong(expected_job, inJob);
+
+		// Regardless of who wrote the slot, we will update the tail (if the successful thread got scheduled out 
+		// after writing the pointer we still want to be able to continue)
+		mTail.compare_exchange_strong(old_value, old_value + 1);
+
+		// If we successfully added our job we're done
+		if (success)
+			break;
+	}
+}
+
+void JobSystemThreadPool::QueueJob(Job *inJob)
+{
+	JPH_PROFILE_FUNCTION();
+
+	// If we have no worker threads, we can't queue the job either. We assume in this case that the job will be added to a barrier and that the barrier will execute the job when it's Wait() function is called.
+	if (mThreads.empty())
+		return;
+
+	// Queue the job
+	QueueJobInternal(inJob);
+
+	// Wake up thread
+	mSemaphore.Release();
+}
+
+void JobSystemThreadPool::QueueJobs(Job **inJobs, uint inNumJobs)
+{
+	JPH_PROFILE_FUNCTION();
+
+	JPH_ASSERT(inNumJobs > 0);
+
+	// If we have no worker threads, we can't queue the job either. We assume in this case that the job will be added to a barrier and that the barrier will execute the job when it's Wait() function is called.
+	if (mThreads.empty())
+		return;
+
+	// Queue all jobs
+	for (Job **job = inJobs, **job_end = inJobs + inNumJobs; job < job_end; ++job)
+		QueueJobInternal(*job);
+
+	// Wake up threads
+	mSemaphore.Release(min(inNumJobs, (uint)mThreads.size()));
+}
+
+#ifdef JPH_PLATFORM_WINDOWS
+
+// Sets the current thread name in MSVC debugger
+static void SetThreadName(const char *inName)
+{
+	#pragma pack(push, 8)
+
+	struct THREADNAME_INFO
+	{
+		DWORD	dwType;			// Must be 0x1000.
+		LPCSTR	szName;			// Pointer to name (in user addr space).
+		DWORD	dwThreadID;		// Thread ID (-1=caller thread).
+		DWORD	dwFlags;		// Reserved for future use, must be zero.
+	};
+
+	#pragma pack(pop)
+
+	THREADNAME_INFO info;
+	info.dwType = 0x1000;
+	info.szName = inName;
+	info.dwThreadID = (DWORD)-1;
+	info.dwFlags = 0;
+
+	__try
+	{
+		RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
+	}
+	__except(EXCEPTION_EXECUTE_HANDLER)
+	{
+	}
+}
+
+#endif
+
+void JobSystemThreadPool::ThreadMain(const string &inName, int inThreadIndex)
+{
+#ifdef JPH_PLATFORM_WINDOWS
+	SetThreadName(inName.c_str());
+#endif
+
+	// Enable floating point exceptions
+	FPExceptionsEnable enable_exceptions;
+	JPH_UNUSED(enable_exceptions);
+
+	JPH_PROFILE_THREAD_START(inName);
+
+	atomic<uint> &head = mHeads[inThreadIndex];
+
+	while (!mQuit)
+	{
+		// Wait for jobs
+		mSemaphore.Acquire();
+
+		{
+			JPH_PROFILE("Executing Jobs");
+
+			// Loop over the queue
+			while (head != mTail)
+			{
+				// Exchange any job pointer we find with a nullptr
+				atomic<Job *> &job = mQueue[head & (cQueueLength - 1)];
+				if (job.load() != nullptr)
+				{
+					Job *job_ptr = job.exchange(nullptr);
+					if (job_ptr != nullptr)
+					{
+						// And execute it
+						job_ptr->Execute();
+						job_ptr->Release();
+					}
+				}
+				head++;
+			}
+		}
+	}
+
+	JPH_PROFILE_THREAD_END();
+}
+
+} // JPH

+ 150 - 0
Jolt/Core/JobSystemThreadPool.h

@@ -0,0 +1,150 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/JobSystem.h>
+#include <Core/FixedSizeFreeList.h>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+
+namespace JPH {
+
+/// Implementation of a JobSystem using a thread pool
+/// 
+/// Note that this is considered an example implementation. It is expected that when you integrate
+/// the physics engine into your own project that you'll provide your own implementation of the
+/// JobSystem built on top of whatever job system your project uses.
+class JobSystemThreadPool final : public JobSystem
+{
+public:
+	/// Creates a thread pool.
+	/// @param inMaxJobs Max number of jobs that can be allocated at any time
+	/// @param inMaxBarriers Max number of barriers that can be allocated at any time
+	/// @param inNumThreads Number of threads to start (the number of concurrent jobs is 1 more because the main thread will also run jobs while waiting for a barrier to complete). Use -1 to autodetect the amount of CPU's.
+							JobSystemThreadPool(uint inMaxJobs, uint inMaxBarriers, int inNumThreads = -1);
+	virtual					~JobSystemThreadPool() override;
+
+	// See JobSystem
+	virtual int				GetMaxConcurrency() const override				{ return int(mThreads.size()) + 1; }
+	virtual JobHandle		CreateJob(const char *inName, ColorArg inColor, const JobFunction &inJobFunction, uint32 inNumDependencies = 0) override;
+	virtual Barrier *		CreateBarrier() override;
+	virtual void			DestroyBarrier(Barrier *inBarrier) override;
+	virtual void			WaitForJobs(Barrier *inBarrier) override;
+
+	/// Change the max concurrency after initialization
+	void					SetNumThreads(int inNumThreads)					{ StopThreads(); StartThreads(inNumThreads); }
+	
+protected:
+	// See JobSystem
+	virtual void			QueueJob(Job *inJob) override;
+	virtual void			QueueJobs(Job **inJobs, uint inNumJobs) override;
+	virtual void			FreeJob(Job *inJob) override;
+
+private:
+	/// When we switch to C++20 we can use counting_semaphore to unify this
+	class Semaphore
+	{
+	public:
+		/// Constructor
+		inline				Semaphore();
+		inline				~Semaphore();
+
+		/// Release the semaphore, signalling the thread waiting on the barrier that there may be work
+		inline void			Release(uint inNumber = 1);
+
+		/// Acquire the semaphore inNumber times
+		inline void			Acquire(uint inNumber = 1);
+
+		/// Get the current value of the semaphore
+		inline int			GetValue() const								{ return mCount; }
+
+	private:
+#ifdef JPH_PLATFORM_WINDOWS
+		// On windows we use a semaphore object since it is more efficient than a lock and a condition variable
+		alignas(JPH_CACHE_LINE_SIZE) atomic<int> mCount { 0 };				///< We increment mCount for every release, to acquire we decrement the count. If the count is negative we know that we are waiting on the actual semaphore.
+		void *				mSemaphore;										///< The semaphore is an expensive construct so we only acquire/release it if we know that we need to wait/have waiting threads
+#else
+		// Other platforms: Emulate a semaphore using a mutex, condition variable and count
+		mutex				mLock;
+		condition_variable	mWaitVariable;
+		int					mCount = 0;
+#endif
+	};
+
+	class BarrierImpl : public Barrier
+	{
+	public:
+		/// Constructor
+							BarrierImpl();
+		virtual				~BarrierImpl() override;
+
+		// See Barrier
+		virtual void		AddJob(const JobHandle &inJob) override;
+		virtual void		AddJobs(const JobHandle *inHandles, uint inNumHandles) override;
+
+		/// Check if there are any jobs in the job barrier
+		inline bool			IsEmpty() const									{ return mJobReadIndex == mJobWriteIndex; }
+
+		/// Wait for all jobs in this job barrier, while waiting, execute jobs that are part of this barrier on the current thread
+		void				Wait();
+
+		/// Flag to indicate if a barrier has been handed out
+		atomic<bool>		mInUse { false };
+
+	protected:
+		/// Called by a Job to mark that it is finished
+		virtual void		OnJobFinished(Job *inJob) override;
+
+		/// Jobs queue for the barrier
+		static constexpr uint cMaxJobs = 1024;
+		static_assert(IsPowerOf2(cMaxJobs));								// We do bit operations and require max jobs to be a power of 2
+		atomic<Job *> 		mJobs[cMaxJobs];								///< List of jobs that are part of this barrier, nullptrs for empty slots
+		alignas(JPH_CACHE_LINE_SIZE) atomic<uint> mJobReadIndex { 0 };		///< First job that could be valid (modulo cMaxJobs), can be nullptr if other thread is still working on adding the job
+		alignas(JPH_CACHE_LINE_SIZE) atomic<uint> mJobWriteIndex { 0 };		///< First job that can be written (modulo cMaxJobs)
+		atomic<int>			mNumToAcquire { 0 };							///< Number of times the semaphore has been released, the barrier should acquire the semaphore this many times (written at the same time as mJobWriteIndex so ok to put in same cache line)
+		Semaphore			mSemaphore;										///< Semaphore used by finishing jobs to signal the barrier that they're done
+	};
+
+	/// Start/stop the worker threads
+	void					StartThreads(int inNumThreads);
+	void					StopThreads();
+	
+	/// Entry point for a thread
+	void					ThreadMain(const string &inName, int inThreadIndex);
+
+	/// Get the head of the thread that has processed the least amount of jobs
+	inline uint				GetHead() const;
+
+	/// Internal helper function to queue a job
+	inline void				QueueJobInternal(Job *inJob);
+
+	/// Array of jobs (fixed size)
+	using AvailableJobs = FixedSizeFreeList<Job>;
+	AvailableJobs			mJobs;
+
+	/// Array of barriers (we keep them constructed all the time since constructing a semaphore/mutex is not cheap)
+	uint					mMaxBarriers;									///< Max amount of barriers
+	BarrierImpl *			mBarriers;										///< List of the actual barriers
+
+	/// Threads running jobs
+	vector<thread>			mThreads;
+
+	// The job queue
+	static constexpr uint32 cQueueLength = 1024;
+	static_assert(IsPowerOf2(cQueueLength));								// We do bit operations and require queue length to be a power of 2
+	atomic<Job *>			mQueue[cQueueLength];
+
+	// Head and tail of the queue, do this value modulo cQueueLength - 1 to get the element in the mQueue array
+	atomic<uint> *			mHeads = nullptr;								///< Per executing thread the head of the current queue
+	alignas(JPH_CACHE_LINE_SIZE) atomic<uint> mTail = 0;					///< Tail (write end) of the queue
+
+	// Semaphore used to signal worker threads that there is new work
+	Semaphore				mSemaphore;
+
+	/// Boolean to indicate that we want to stop the job system
+	atomic<bool>			mQuit = false;
+};
+
+} // JPH

+ 50 - 0
Jolt/Core/LinearCurve.cpp

@@ -0,0 +1,50 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/LinearCurve.h>
+#include <Core/StreamIn.h>
+#include <Core/StreamOut.h>
+#include <ObjectStream/TypeDeclarations.h>
+
+namespace JPH {
+
+JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(LinearCurve::Point)
+{
+	JPH_ADD_ATTRIBUTE(Point, mX)
+	JPH_ADD_ATTRIBUTE(Point, mY)
+}
+
+JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(LinearCurve)
+{
+	JPH_ADD_ATTRIBUTE(LinearCurve, mPoints)
+}
+
+float LinearCurve::GetValue(float inX) const
+{
+	if (mPoints.empty())
+		return 0.0f;
+
+	Points::const_iterator i2 = lower_bound(mPoints.begin(), mPoints.end(), inX, [](const Point &inPoint, float inValue) { return inPoint.mX < inValue; });
+
+	if (i2 == mPoints.begin())
+		return mPoints.front().mY;
+	else if (i2 == mPoints.end())
+		return mPoints.back().mY;
+
+	Points::const_iterator i1 = i2 - 1;
+	return i1->mY + (inX - i1->mX) * (i2->mY - i1->mY) / (i2->mX - i1->mX);
+}
+
+void LinearCurve::SaveBinaryState(StreamOut &inStream) const
+{
+	inStream.Write(mPoints);
+}
+
+void LinearCurve::RestoreBinaryState(StreamIn &inStream)
+{
+	inStream.Read(mPoints);
+}
+
+} // JPH

+ 65 - 0
Jolt/Core/LinearCurve.h

@@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <ObjectStream/SerializableObject.h>
+
+namespace JPH {
+
+class StreamOut;
+class StreamIn;
+
+// A set of points (x, y) that form a linear curve
+class LinearCurve
+{
+public:
+	JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(LinearCurve)
+
+	/// A point on the curve
+	class Point
+	{
+	public:
+		JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(Point)
+
+		float			mX = 0.0f;
+		float			mY = 0.0f;
+	};
+
+	/// Remove all points
+	void				Clear()											{ mPoints.clear(); }
+
+	/// Reserve memory for inNumPoints points
+	void				Reserve(uint inNumPoints)						{ mPoints.reserve(inNumPoints); }
+
+	/// Add a point to the curve. Points must be inserted in ascending X or Sort() needs to be called when all points have been added.
+	/// @param inX X value
+	/// @param inY Y value
+	void				AddPoint(float inX, float inY)					{ mPoints.push_back({ inX, inY }); }
+
+	/// Sort the points on X ascending
+	void				Sort()											{ sort(mPoints.begin(), mPoints.end(), [](const Point &inLHS, const Point &inRHS) { return inLHS.mX < inRHS.mX; }); }
+
+	/// Get the lowest X value
+	float				GetMinX() const									{ return mPoints.empty()? 0.0f : mPoints.front().mX; }
+
+	/// Get the highest X value
+	float				GetMaxX() const									{ return mPoints.empty()? 0.0f : mPoints.back().mX; }
+
+	/// Sample value on the curve
+	/// @param inX X value to sample at
+	/// @return Interpolated Y value
+	float				GetValue(float inX) const;
+
+	/// Saves the state of this object in binary form to inStream.
+	void				SaveBinaryState(StreamOut &inStream) const;
+
+	/// Restore the state of this object from inStream.
+	void				RestoreBinaryState(StreamIn &inStream);
+
+	/// The points on the curve, should be sorted ascending by x
+	using Points = vector<Point>;
+	Points				mPoints;
+};
+
+} // JPH

+ 121 - 0
Jolt/Core/LockFreeHashMap.h

@@ -0,0 +1,121 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/NonCopyable.h>
+#include <atomic>
+
+namespace JPH {
+
+/// Very simple lock free hash map that only allows insertion, retrieval and provides a fixed amount of buckets and fixed storage.
+/// Note: This class currently assumes key and value are simple types that need no calls to the destructor.
+template <class Key, class Value>
+class LockFreeHashMap : public NonCopyable
+{
+public:
+	using MapType = LockFreeHashMap<Key, Value>;
+
+	/// Destructor
+							~LockFreeHashMap();
+
+	/// Initialization
+	/// @param inObjectStoreSizeBytes Number of bytes to reserve for all key value pairs
+	/// @param inMaxBuckets Max amount of buckets to use in the hashmap. Must be power of 2.
+	void					Init(uint32 inObjectStoreSizeBytes, uint32 inMaxBuckets);
+
+	/// Remove all elements.
+	/// Note that this cannot happen simultaneously with adding new elements.
+	void					Clear();
+	
+	/// Get the current amount of buckets that the map is using
+	uint32					GetNumBuckets() const			{ return mNumBuckets; }
+
+	/// Get the maximum amount of buckets that this map supports
+	uint32					GetMaxBuckets() const			{ return mMaxBuckets; }
+
+	/// Update the number of buckets. This must be done after clearing the map and cannot be done concurrently with any other operations on the map.
+	/// Note that the number of buckets can never become bigger than the specified max buckets during initialization and that it must be a power of 2.
+	void					SetNumBuckets(uint32 inNumBuckets);
+
+	/// A key / value pair that is inserted in the map
+	class KeyValue
+	{
+	public:
+		const Key &			GetKey() const					{ return mKey; }
+		Value &				GetValue()						{ return mValue; }
+		const Value &		GetValue() const				{ return mValue; }
+
+	private:
+		template <class K, class V> friend class LockFreeHashMap;
+
+		Key					mKey;							///< Key for this entry
+		uint32				mNextOffset;					///< Offset in mObjectStore of next KeyValue entry with same hash
+		Value				mValue;							///< Value for this entry + optionally extra bytes
+	};
+
+	/// Insert a new element, returns null if map full.
+	/// Multiple threads can be inserting in the map at the same time.
+	template <class... Params>
+	inline KeyValue *		Create(const Key &inKey, size_t inKeyHash, int inExtraBytes, Params &&... inConstructorParams);
+	
+	/// Find an element, returns null if not found
+	inline const KeyValue *	Find(const Key &inKey, size_t inKeyHash) const;
+
+	/// Value of an invalid handle
+	const static uint32		cInvalidHandle = uint32(-1);
+
+	/// Get convert key value pair to uint32 handle
+	inline uint32			ToHandle(const KeyValue *inKeyValue) const;
+
+	/// Convert uint32 handle back to key and value
+	inline const KeyValue *	FromHandle(uint32 inHandle) const;
+
+	/// Get the number of key value pairs that this map currently contains
+	inline uint32			GetNumKeyValues() const		{ return mNumKeyValues; }
+
+	/// Get all key/value pairs
+	inline void				GetAllKeyValues(vector<const KeyValue *> &outAll) const;
+
+	/// Non-const iterator
+	struct Iterator
+	{
+		/// Comparison
+		bool				operator == (const Iterator &inRHS) const	{ return mMap == inRHS.mMap && mBucket == inRHS.mBucket && mOffset == inRHS.mOffset; }
+		bool				operator != (const Iterator &inRHS) const	{ return !(*this == inRHS); }
+
+		/// Convert to key value pair
+		KeyValue & 			operator * ();
+
+		/// Next item
+		Iterator &			operator ++ ();
+
+		MapType *			mMap;		
+		uint32				mBucket;
+		uint32				mOffset;
+	};
+
+	/// Iterate over the map, note that it is not safe to do this in parallel to Clear(). 
+	/// It is safe to do this while adding elements to the map, but newly added elements may or may not be returned by the iterator.
+	Iterator				begin();
+	Iterator				end();
+
+#ifdef _DEBUG
+	/// Output stats about this map to the log
+	void					TraceStats() const;
+#endif
+
+private:
+	uint8 *					mObjectStore = nullptr;			///< This contains a contigous list of objects (possibly of varying size)
+	uint32					mObjectStoreSizeBytes = 0;		///< The size of mObjectStore in bytes
+	atomic<uint32>			mWriteOffset { 0 };				///< Next offset to write to in mObjectStore
+	atomic<uint32>			mNumKeyValues = 0;				///< Number of key value pairs in the store
+
+	atomic<uint32> *		mBuckets = nullptr;				///< This contains the offset in mObjectStore of the first object with a particular hash
+	uint32					mNumBuckets = 0;				///< Current number of buckets
+	uint32					mMaxBuckets = 0;				///< Maximum number of buckets
+};
+
+} // JPH
+
+#include <Core/LockFreeHashMap.inl>

+ 242 - 0
Jolt/Core/LockFreeHashMap.inl

@@ -0,0 +1,242 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+template <class Key, class Value>
+void LockFreeHashMap<Key, Value>::Init(uint inObjectStoreSizeBytes, uint32 inMaxBuckets)
+{
+	JPH_ASSERT(inMaxBuckets >= 4 && IsPowerOf2(inMaxBuckets));
+	JPH_ASSERT(mObjectStore == nullptr);
+	JPH_ASSERT(mBuckets == nullptr);
+
+	mObjectStoreSizeBytes = inObjectStoreSizeBytes;
+	mNumBuckets = inMaxBuckets;
+	mMaxBuckets = inMaxBuckets;
+
+	mObjectStore = new uint8 [inObjectStoreSizeBytes];
+	mBuckets = new atomic<uint32> [inMaxBuckets];
+
+	Clear();
+}
+
+template <class Key, class Value>
+LockFreeHashMap<Key, Value>::~LockFreeHashMap()
+{
+	delete [] mObjectStore;
+	delete [] mBuckets;
+}
+
+template <class Key, class Value>
+void LockFreeHashMap<Key, Value>::Clear()
+{
+	// Reset write offset and number of key value pairs
+	mWriteOffset = 0;
+	mNumKeyValues = 0;
+
+	// Reset buckets 4 at a time
+	static_assert(sizeof(atomic<uint32>) == sizeof(uint32));
+	UVec4 invalid_handle = UVec4::sReplicate(cInvalidHandle);
+	uint32 *start = reinterpret_cast<uint32 *>(mBuckets), *end = start + mNumBuckets;
+	JPH_ASSERT(IsAligned(start, 16));
+	while (start < end)
+	{
+		invalid_handle.StoreInt4Aligned(start);
+		start += 4;
+	}
+}
+
+template <class Key, class Value>
+void LockFreeHashMap<Key, Value>::SetNumBuckets(uint32 inNumBuckets)
+{
+	JPH_ASSERT(mNumKeyValues == 0);
+	JPH_ASSERT(inNumBuckets <= mMaxBuckets);
+	JPH_ASSERT(inNumBuckets >= 4 && IsPowerOf2(inNumBuckets));
+
+	mNumBuckets = inNumBuckets;	
+}
+
+template <class Key, class Value>
+template <class... Params>
+inline typename LockFreeHashMap<Key, Value>::KeyValue *LockFreeHashMap<Key, Value>::Create(const Key &inKey, size_t inKeyHash, int inExtraBytes, Params &&... inConstructorParams)
+{
+	// This is not a multi map, test the key hasn't been inserted yet
+	JPH_ASSERT(Find(inKey, inKeyHash) == nullptr);
+
+	// Calculate total size
+	uint size = sizeof(KeyValue) + inExtraBytes;
+
+	// Allocate entry in the cache
+	uint32 write_offset = mWriteOffset.fetch_add(size);
+	if (write_offset + size > mObjectStoreSizeBytes)
+		return nullptr;
+	++mNumKeyValues;
+
+	// Construct the key/value pair
+	KeyValue *kv = reinterpret_cast<KeyValue *>(mObjectStore + write_offset);
+#ifdef _DEBUG
+	memset(kv, 0xcd, size);
+#endif
+	kv->mKey = inKey;
+	new (&kv->mValue) Value(forward<Params>(inConstructorParams)...);
+
+	// Get the offset to the first object from the bucket with corresponding hash
+	atomic<uint32> &offset = mBuckets[inKeyHash & (mNumBuckets - 1)];
+
+	// Add this entry as the first element in the linked list
+	uint32 new_offset = uint32(reinterpret_cast<uint8 *>(kv) - mObjectStore);
+	for (;;)
+	{
+		uint32 old_offset = offset;
+		kv->mNextOffset = old_offset;
+		if (offset.compare_exchange_strong(old_offset, new_offset))
+			break;
+	}
+
+	return kv;
+}
+
+template <class Key, class Value>
+inline const typename LockFreeHashMap<Key, Value>::KeyValue *LockFreeHashMap<Key, Value>::Find(const Key &inKey, size_t inKeyHash) const
+{
+	// Get the offset to the keyvalue object from the bucket with corresponding hash
+	uint32 offset = mBuckets[inKeyHash & (mNumBuckets - 1)];
+	while (offset != cInvalidHandle)
+	{
+		// Loop through linked list of values until the right one is found
+		const KeyValue *kv = reinterpret_cast<const KeyValue *>(mObjectStore + offset);
+		if (kv->mKey == inKey)
+			return kv;
+		offset = kv->mNextOffset;
+	}
+
+	// Not found
+	return nullptr;
+}
+
+template <class Key, class Value>
+inline uint32 LockFreeHashMap<Key, Value>::ToHandle(const KeyValue *inKeyValue) const
+{
+	const uint8 *kv = reinterpret_cast<const uint8 *>(inKeyValue);
+	JPH_ASSERT(kv >= mObjectStore && kv < mObjectStore + mObjectStoreSizeBytes);
+	return uint32(kv - mObjectStore);
+}
+
+template <class Key, class Value>
+inline const typename LockFreeHashMap<Key, Value>::KeyValue *LockFreeHashMap<Key, Value>::FromHandle(uint32 inHandle) const
+{
+	JPH_ASSERT(inHandle < mObjectStoreSizeBytes);
+	return reinterpret_cast<const KeyValue *>(mObjectStore + inHandle);
+}
+
+template <class Key, class Value>
+inline void LockFreeHashMap<Key, Value>::GetAllKeyValues(vector<const KeyValue *> &outAll) const
+{
+	for (atomic<uint32> *bucket = mBuckets; bucket < mBuckets + mNumBuckets; ++bucket)
+	{
+		uint32 offset = *bucket;
+		while (offset != cInvalidHandle)
+		{
+			const KeyValue *kv = reinterpret_cast<const KeyValue *>(mObjectStore + offset);
+			outAll.push_back(kv);
+			offset = kv->mNextOffset;
+		}
+	}
+}
+
+template <class Key, class Value>
+typename LockFreeHashMap<Key, Value>::Iterator LockFreeHashMap<Key, Value>::begin()
+{
+	// Start with the first bucket
+	Iterator it { this, 0, mBuckets[0] };
+
+	// If it doesn't contain a valid entry, use the ++ operator to find the first valid entry
+	if (it.mOffset == cInvalidHandle)
+		++it;
+
+	return it;
+}
+
+template <class Key, class Value>
+typename LockFreeHashMap<Key, Value>::Iterator LockFreeHashMap<Key, Value>::end()
+{
+	return { this, mNumBuckets, cInvalidHandle };
+}
+
+template <class Key, class Value>
+typename LockFreeHashMap<Key, Value>::KeyValue &LockFreeHashMap<Key, Value>::Iterator::operator* ()
+{
+	JPH_ASSERT(mOffset != cInvalidHandle);
+
+	return *reinterpret_cast<KeyValue *>(mMap->mObjectStore + mOffset);
+}		
+
+template <class Key, class Value>
+typename LockFreeHashMap<Key, Value>::Iterator &LockFreeHashMap<Key, Value>::Iterator::operator++ ()
+{
+	JPH_ASSERT(mBucket < mMap->mNumBuckets);
+
+	// Find the next key value in this bucket
+	if (mOffset != cInvalidHandle)
+	{
+		const KeyValue *kv = reinterpret_cast<const KeyValue *>(mMap->mObjectStore + mOffset);
+		mOffset = kv->mNextOffset;
+		if (mOffset != cInvalidHandle)
+			return *this;
+	}
+
+	// Loop over next buckets
+	for (;;)
+	{
+		// Next bucket
+		++mBucket;
+		if (mBucket >= mMap->mNumBuckets)
+			return *this;
+
+		// Fetch the first entry in the bucket
+		mOffset = mMap->mBuckets[mBucket];
+		if (mOffset != cInvalidHandle)
+			return *this;
+	}
+}
+
+#ifdef _DEBUG
+
+template <class Key, class Value>
+void LockFreeHashMap<Key, Value>::TraceStats() const
+{
+	const int cMaxPerBucket = 256;
+
+	int max_objects_per_bucket = 0;
+	int num_objects = 0;
+	int histogram[cMaxPerBucket];
+	for (int i = 0; i < cMaxPerBucket; ++i)
+		histogram[i] = 0;
+
+	for (atomic<uint32> *bucket = mBuckets, *bucket_end = mBuckets + mNumBuckets; bucket < bucket_end; ++bucket)
+	{
+		int objects_in_bucket = 0;
+		uint32 offset = *bucket;
+		while (offset != cInvalidHandle)
+		{
+			const KeyValue *kv = reinterpret_cast<const KeyValue *>(mObjectStore + offset);
+			offset = kv->mNextOffset;
+			++objects_in_bucket;
+			++num_objects;
+		}
+		max_objects_per_bucket = max(objects_in_bucket, max_objects_per_bucket);
+		histogram[min(objects_in_bucket, cMaxPerBucket - 1)]++;
+	}
+
+	Trace("max_objects_per_bucket = %d, num_buckets = %d, num_objects = %d", max_objects_per_bucket, mNumBuckets, num_objects);
+	
+	for (int i = 0; i < cMaxPerBucket; ++i)
+		if (histogram[i] != 0)
+			Trace("%d: %d", i, histogram[i]);
+}
+
+#endif
+
+} // JPH

+ 35 - 0
Jolt/Core/Memory.cpp

@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/Memory.h>
+#include <cstdlib>
+#include <stdlib.h>
+
+namespace JPH {
+
+void *AlignedAlloc(size_t inSize, size_t inAlignment)
+{
+#if defined(JPH_PLATFORM_WINDOWS)
+	// Microsoft doesn't implement C++17 std::aligned_alloc
+	return _aligned_malloc(inSize, inAlignment);
+#elif defined(JPH_PLATFORM_ANDROID)
+	return memalign(inAlignment, AlignUp(inSize, inAlignment));
+#else
+	return std::aligned_alloc(inAlignment, AlignUp(inSize, inAlignment));
+#endif
+}
+
+void AlignedFree(void *inBlock)
+{
+#if defined(JPH_PLATFORM_WINDOWS)
+	_aligned_free(inBlock);
+#elif defined(JPH_PLATFORM_ANDROID)
+	free(inBlock);
+#else
+	std::free(inBlock);
+#endif
+}
+
+} // JPH

+ 14 - 0
Jolt/Core/Memory.h

@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// Allocate a block of memory aligned to inAlignment bytes of size inSize
+void *AlignedAlloc(size_t inSize, size_t inAlignment);
+
+/// Free memory block allocated with AlignedAlloc
+void AlignedFree(void *inBlock);
+
+} // JPH

+ 119 - 0
Jolt/Core/Mutex.h

@@ -0,0 +1,119 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <mutex>
+#include <shared_mutex>
+#include <thread>
+#include <Core/Profiler.h>
+
+namespace JPH {
+
+#if defined(JPH_ENABLE_ASSERTS) || defined(JPH_PROFILE_ENABLED) || defined(JPH_EXTERNAL_PROFILE)
+
+/// Very simple wrapper around std::mutex which tracks lock contention in the profiler 
+/// and asserts that locks/unlocks take place on the same thread
+class Mutex : public mutex
+{
+public:
+	inline bool		try_lock()
+	{
+		JPH_ASSERT(mLockedThreadID != this_thread::get_id());
+		if (mutex::try_lock())
+		{
+			JPH_IF_ENABLE_ASSERTS(mLockedThreadID = this_thread::get_id();)
+			return true;
+		}
+		return false;
+	}
+
+	inline void		lock()
+	{
+		if (!try_lock())
+		{
+			JPH_PROFILE("Lock", 0xff00ffff);
+			mutex::lock();
+			JPH_IF_ENABLE_ASSERTS(mLockedThreadID = this_thread::get_id();)
+		}
+	}
+
+	inline void		unlock()
+	{
+		JPH_ASSERT(mLockedThreadID == this_thread::get_id());
+		JPH_IF_ENABLE_ASSERTS(mLockedThreadID = thread::id();)
+		mutex::unlock();
+	}
+
+#ifdef JPH_ENABLE_ASSERTS
+	inline bool		is_locked()
+	{
+		return mLockedThreadID != thread::id();
+	}
+#endif // JPH_ENABLE_ASSERTS
+
+private:
+	JPH_IF_ENABLE_ASSERTS(thread::id mLockedThreadID;)
+};
+
+/// Very simple wrapper around std::shared_mutex which tracks lock contention in the profiler
+/// and asserts that locks/unlocks take place on the same thread
+class SharedMutex : public shared_mutex
+{
+public:
+	inline bool		try_lock()
+	{
+		JPH_ASSERT(mLockedThreadID != this_thread::get_id());
+		if (shared_mutex::try_lock())
+		{
+			JPH_IF_ENABLE_ASSERTS(mLockedThreadID = this_thread::get_id();)
+			return true;
+		}
+		return false;
+	}
+
+	inline void		lock()
+	{
+		if (!try_lock())
+		{
+			JPH_PROFILE("Lock", 0xff00ffff);
+			shared_mutex::lock();
+			JPH_IF_ENABLE_ASSERTS(mLockedThreadID = this_thread::get_id();)
+		}
+	}
+
+	inline void		unlock()
+	{
+		JPH_ASSERT(mLockedThreadID == this_thread::get_id());
+		JPH_IF_ENABLE_ASSERTS(mLockedThreadID = thread::id();)
+		shared_mutex::unlock();
+	}
+
+#ifdef JPH_ENABLE_ASSERTS
+	inline bool		is_locked()
+	{
+		return mLockedThreadID != thread::id();
+	}
+#endif // JPH_ENABLE_ASSERTS
+
+	inline void		lock_shared()
+	{
+		if (!try_lock_shared())
+		{
+			JPH_PROFILE("LockShared", 0xff00ffff);
+			shared_mutex::lock_shared();
+		}
+	}
+
+private:
+	JPH_IF_ENABLE_ASSERTS(thread::id mLockedThreadID;)
+};
+
+#else
+
+using Mutex = mutex;
+using SharedMutex = shared_mutex;
+
+#endif
+
+} // JPH

+ 68 - 0
Jolt/Core/MutexArray.h

@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// A mutex array protects a number of resources with a limited amount of mutexes.
+/// It uses hashing to find the mutex of a particular object.
+/// The idea is that if the amount of threads is much smaller than the amount of mutexes
+/// that there is a relatively small chance that two different objects map to the same mutex.
+template <class MutexType, int NumMutexesArg>
+class MutexArray
+{
+public:
+	/// Number of mutexes used to protect the underlying resources.
+	static constexpr int	NumMutexes = NumMutexesArg;
+
+	/// Convert an object index to a mutex index
+	inline uint32			GetMutexIndex(uint32 inObjectIndex) const
+	{
+		std::hash<uint32> hasher;
+		static_assert(IsPowerOf2(NumMutexes), "Number of mutexes must be power of 2");
+		return hasher(inObjectIndex) & (NumMutexes - 1);
+	}
+
+	/// Get the mutex belonging to a certain object by index
+	inline MutexType &		GetMutexByObjectIndex(uint32 inObjectIndex)
+	{
+		return mMutexStorage[GetMutexIndex(inObjectIndex)].mMutex;
+	}
+
+	/// Get a mutex by index in the array
+	inline MutexType &		GetMutexByIndex(uint32 inMutexIndex)
+	{
+		return mMutexStorage[inMutexIndex].mMutex;
+	}
+
+	/// Lock all mutexes
+	void					LockAll()
+	{
+		JPH_PROFILE_FUNCTION();
+
+		for (MutexStorage &m : mMutexStorage)
+			m.mMutex.lock();
+	}
+
+	/// Unlock all mutexes
+	void					UnlockAll()
+	{
+		JPH_PROFILE_FUNCTION();
+
+		for (MutexStorage &m : mMutexStorage)
+			m.mMutex.unlock();
+	}
+
+private:
+	/// Align the mutex to a cache line to ensure there is no false sharing (this is platform dependent, we do this to be safe)
+	struct alignas(JPH_CACHE_LINE_SIZE) MutexStorage
+	{
+		MutexType			mMutex;
+	};
+
+	MutexStorage			mMutexStorage[NumMutexes];
+};
+
+} // JPH
+

+ 17 - 0
Jolt/Core/NonCopyable.h

@@ -0,0 +1,17 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// Class that makes another class non-copyable. Usage: Inherit from NonCopyable.
+class NonCopyable
+{
+public:
+	inline	NonCopyable() { }
+			NonCopyable(const NonCopyable &) = delete;
+	void	operator = (const NonCopyable &) = delete;
+};
+
+} // JPH

+ 384 - 0
Jolt/Core/Profiler.cpp

@@ -0,0 +1,384 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/Profiler.h>
+#include <Core/Color.h>
+#include <Core/StringTools.h>
+#include <fstream>
+
+namespace JPH {
+
+#ifdef JPH_PROFILE_ENABLED
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Profiler
+//////////////////////////////////////////////////////////////////////////////////////////
+
+Profiler Profiler::sInstance;
+thread_local ProfileThread *ProfileThread::sInstance = nullptr;
+
+bool ProfileMeasurement::sOutOfSamplesReported = false;
+
+void Profiler::NextFrame()
+{
+	lock_guard<mutex> lock(mLock);
+
+	if (mDump)
+	{
+		DumpInternal();
+		mDump = false;
+	}
+
+	for (ProfileThread *t : mThreads)
+		t->mCurrentSample = 0;
+}
+
+void Profiler::Dump()
+{
+	mDump = true;
+}
+
+void Profiler::AddThread(ProfileThread *inThread)										
+{ 
+	lock_guard<mutex> lock(mLock); 
+
+	mThreads.push_back(inThread); 
+}
+
+void Profiler::RemoveThread(ProfileThread *inThread)									
+{ 
+	lock_guard<mutex> lock(mLock); 
+	
+	vector<ProfileThread *>::iterator i = find(mThreads.begin(), mThreads.end(), inThread); 
+	JPH_ASSERT(i != mThreads.end()); 
+	mThreads.erase(i); 
+}
+
+void Profiler::sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator)
+{
+	// Store depth
+	ioSample->mDepth = uint8(min(255, inDepth));
+
+	// Update color
+	if (ioSample->mColor == 0)
+		ioSample->mColor = inColor;
+	else
+		inColor = ioSample->mColor;
+
+	// Start accumulating totals
+	uint64 cycles_this_with_children = ioSample->mEndCycle - ioSample->mStartCycle;
+	uint64 cycles_in_children = 0;
+
+	// Loop over following samples until we find a sample that starts on or after our end
+	ProfileSample *sample;
+	for (sample = ioSample + 1; sample < inEnd && sample->mStartCycle < ioSample->mEndCycle; ++sample)
+	{
+		JPH_ASSERT(sample[-1].mStartCycle <= sample->mStartCycle);
+		JPH_ASSERT(sample->mStartCycle >= ioSample->mStartCycle);
+		JPH_ASSERT(sample->mEndCycle <= ioSample->mEndCycle);
+
+		// This is a direct child of us, accumulate time
+		cycles_in_children += sample->mEndCycle - sample->mStartCycle;
+
+		// Recurse and skip over the children of this child
+		sAggregate(inDepth + 1, inColor, sample, inEnd, ioAggregators, ioKeyToAggregator);
+	}
+
+	// Find the aggregator for this name / filename pair
+	Aggregator *aggregator;
+	KeyToAggregator::iterator aggregator_idx = ioKeyToAggregator.find(ioSample->mName);
+	if (aggregator_idx == ioKeyToAggregator.end())
+	{
+		// Not found, add to map and insert in array
+		ioKeyToAggregator.insert(KeyToAggregator::value_type(ioSample->mName, ioAggregators.size()));
+		ioAggregators.emplace_back(ioSample->mName);
+		aggregator = &ioAggregators.back();
+	}
+	else
+	{
+		// Found
+		aggregator = &ioAggregators[aggregator_idx->second];
+	}
+
+	// Add the measurement to the aggregator
+	aggregator->AccumulateMeasurement(cycles_this_with_children, cycles_in_children);
+
+	// Update ioSample to the last child of ioSample
+	JPH_ASSERT(sample[-1].mStartCycle < ioSample->mEndCycle);
+	JPH_ASSERT(sample >= inEnd || sample->mStartCycle >= ioSample->mEndCycle);
+	ioSample = sample - 1;
+}
+
+void Profiler::DumpInternal()
+{
+	// Freeze data from threads
+	// Note that this is not completely thread safe: As a profile sample is added mCurrentSample is incremented
+	// but the data is not written until the sample finishes. So if we dump the profile information while
+	// some other thread is running, we may get some garbage information from the previous frame
+	Threads threads;
+	for (ProfileThread *t : mThreads)
+		threads.push_back({ t->mThreadName, t->mSamples, t->mSamples + t->mCurrentSample });
+	
+	// Shift all samples so that the first sample is at zero
+	uint64 min_cycle = 0xffffffffffffffffUL;
+	for (const ThreadSamples &t : threads)
+		if (t.mSamplesBegin < t.mSamplesEnd)
+			min_cycle = min(min_cycle, t.mSamplesBegin[0].mStartCycle);
+	for (const ThreadSamples &t : threads)
+		for (ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s)
+		{
+			s->mStartCycle -= min_cycle;
+			s->mEndCycle -= min_cycle;
+		}
+
+	// Next sequence number
+	static int number = 0;
+	++number;
+
+	// Aggregate data across threads
+	Aggregators aggregators;
+	KeyToAggregator key_to_aggregators;
+	for (const ThreadSamples &t : threads)
+		for (ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s)
+			sAggregate(0, Color::sGetDistinctColor(0).GetUInt32(), s, end, aggregators, key_to_aggregators);
+
+	// Dump as list
+	DumpList(number, aggregators);
+
+	// Dump as chart
+	DumpChart(number, threads, key_to_aggregators, aggregators);
+}
+
+static string sHTMLEncode(string inString)
+{
+	string str = inString;
+	StringReplace(str, "<", "&lt;");
+	StringReplace(str, ">", "&gt;");
+	return str;
+}
+
+void Profiler::DumpList(int inNumber, const Aggregators &inAggregators)
+{
+	// Open file
+	ofstream f;
+	f.open(StringFormat("profile_list_%d.html", inNumber).c_str(), ofstream::out | ofstream::trunc);
+	if (!f.is_open()) 
+		return;
+
+	// Write header
+	f << R"(<!DOCTYPE html>
+<html>
+	<head>
+		<title>Profile List</title>
+		<link rel="stylesheet" href="WebIncludes/semantic.min.css">
+		<script type="text/javascript" src="WebIncludes/jquery-3.2.1.min.js"></script>
+		<script type="text/javascript" src="WebIncludes/semantic.min.js"></script>
+		<script type="text/javascript" src="WebIncludes/tablesort.js"></script>
+		<script type="text/javascript">$(document).ready(function() { $('table').tablesort({ compare: function(a, b) { return isNaN(a) || isNaN(b)? a.localeCompare(b) : Number(a) - Number(b); } }); });</script>
+	</head>
+	<body class="minimal pushable">
+		<table id="profile" class="ui sortable celled striped table">
+			<thead>
+				<tr>
+					<th>Description</th>
+					<th class="sorted descending">Total time with children (%)</th>
+					<th>Total time (%)</th>
+					<th>Calls</th>
+					<th>&micro;s / call with children</th>
+					<th>&micro;s / call</th>
+					<th>Min. &micro;s / call</th>
+					<th>Max. &micro;s / call</th>
+				</tr>
+			</thead>
+			<tbody style="text-align: right;">
+)";
+
+	// Get total time
+	uint64 total_time = 0;
+	for (const Aggregator &item : inAggregators)
+		total_time += item.mTotalCyclesInCallWithChildren - item.mTotalCyclesInChildren;
+
+	// Get cycles per second
+	uint64 cycles_per_second = GetProcessorTicksPerSecond();
+	
+	// Sort the list
+	Aggregators aggregators = inAggregators;
+	sort(aggregators.begin(), aggregators.end());
+	
+	// Write all aggregators
+	for (const Aggregator &item : aggregators)
+	{
+		uint64 cycles_in_call_no_children = item.mTotalCyclesInCallWithChildren - item.mTotalCyclesInChildren;
+
+		char str[2048];
+		snprintf(str, sizeof(str), R"(<tr>
+	<td style="text-align: left;">%s</td>
+	<td>%.1f</td>						
+	<td>%.1f</td>						
+	<td>%u</td>
+	<td>%.2f</td>						
+	<td>%.2f</td>						
+	<td>%.2f</td>						
+	<td>%.2f</td>						
+</tr>)", 
+			sHTMLEncode(item.mName).c_str(),															// Description
+			100.0 * item.mTotalCyclesInCallWithChildren / total_time,									// Total time with children
+			100.0 * cycles_in_call_no_children / total_time,											// Total time no children
+			item.mCallCounter,																			// Calls
+			1000000.0 * item.mTotalCyclesInCallWithChildren / cycles_per_second / item.mCallCounter,	// us / call with children
+			1000000.0 * cycles_in_call_no_children / cycles_per_second / item.mCallCounter,				// us / call no children
+			1000000.0 * item.mMinCyclesInCallWithChildren / cycles_per_second,							// Min. us / call with children
+			1000000.0 * item.mMaxCyclesInCallWithChildren / cycles_per_second);							// Max. us / call with children
+
+		f << str;
+	}
+
+	// End table
+	f << R"(</tbody></table></body></html>)";
+}
+
+void Profiler::DumpChart(int inNumber, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators)
+{
+	// Open file
+	ofstream f;
+	f.open(StringFormat("profile_chart_%d.html", inNumber).c_str(), ofstream::out | ofstream::trunc);
+	if (!f.is_open()) 
+		return;
+
+	// Write header
+	f << R"(<!DOCTYPE html>
+<html>
+	<head>
+		<title>Profile Chart</title>
+		<link rel="stylesheet" href="WebIncludes/profile_chart.css">
+		<script type="text/javascript" src="WebIncludes/profile_chart.js"></script>
+	</head>
+	<body onload="startChart();">
+	<script type="text/javascript">
+)";
+
+	// Get cycles per second
+	uint64 cycles_per_second = GetProcessorTicksPerSecond();
+	f << "var cycles_per_second = " << cycles_per_second << ";\n";
+
+	// Dump samples
+	f << "var threads = [\n";
+	bool first_thread = true;
+	for (const ThreadSamples &t : inThreads)
+	{
+		if (!first_thread)
+			f << ",\n";
+		first_thread = false;
+
+		f << "{\nthread_name: \"" << t.mThreadName << "\",\naggregator: [";
+		bool first = true;
+		for (const ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s)
+		{
+			if (!first)
+				f << ",";
+			first = false;
+			f << inKeyToAggregators.find(s->mName)->second;
+		}
+		f << "],\ncolor: [";
+		first = true;
+		for (const ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s)
+		{
+			if (!first)
+				f << ",";
+			first = false;
+			Color c(s->mColor);
+			f << StringFormat("\"#%02x%02x%02x\"", c.r, c.g, c.b);
+		}
+		f << "],\nstart: [";
+		first = true;
+		for (const ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s)
+		{
+			if (!first)
+				f << ",";
+			first = false;
+			f << s->mStartCycle;
+		}
+		f << "],\ncycles: [";
+		first = true;
+		for (const ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s)
+		{
+			if (!first)
+				f << ",";
+			first = false;
+			f << s->mEndCycle - s->mStartCycle;
+		}
+		f << "],\ndepth: [";
+		first = true;
+		for (const ProfileSample *s = t.mSamplesBegin, *end = t.mSamplesEnd; s < end; ++s)
+		{
+			if (!first)
+				f << ",";
+			first = false;
+			f << int(s->mDepth);
+		}
+		f << "]\n}";
+	}
+
+	// Dump aggregated data
+	f << "];\nvar aggregated = {\nname: [";
+	bool first = true;
+	for (const Aggregator &a : inAggregators)
+	{
+		if (!first)
+			f << ",";
+		first = false;
+		string name = "\"" + sHTMLEncode(a.mName) + "\"";
+		f << name;
+	}
+	f << "],\ncalls: [";
+	first = true;
+	for (const Aggregator &a : inAggregators)
+	{
+		if (!first)
+			f << ",";
+		first = false;
+		f << a.mCallCounter;
+	}
+	f << "],\nmin_cycles: [";
+	first = true;
+	for (const Aggregator &a : inAggregators)
+	{
+		if (!first)
+			f << ",";
+		first = false;
+		f << a.mMinCyclesInCallWithChildren;
+	}
+	f << "],\nmax_cycles: [";
+	first = true;
+	for (const Aggregator &a : inAggregators)
+	{
+		if (!first)
+			f << ",";
+		first = false;
+		f << a.mMaxCyclesInCallWithChildren;
+	}
+	f << "],\ncycles_per_frame: [";
+	first = true;
+	for (const Aggregator &a : inAggregators)
+	{
+		if (!first)
+			f << ",";
+		first = false;
+		f << a.mTotalCyclesInCallWithChildren;
+	}
+
+	// Write footer
+	f << R"(]};
+</script>
+
+<canvas id="canvas"></canvas>
+<div id="tooltip"></div>
+
+</tbody></table></body></html>)";
+}
+
+#endif // JPH_PROFILE_ENABLED
+
+} // JPH

+ 232 - 0
Jolt/Core/Profiler.h

@@ -0,0 +1,232 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <mutex>
+#include <unordered_map>
+
+#include <Core/NonCopyable.h>
+#include <Core/TickCounter.h>
+
+#if defined(JPH_EXTERNAL_PROFILE)
+
+namespace JPH {
+
+/// Create this class on the stack to start sampling timing information of a particular scope.
+///
+/// Left unimplemented intentionally. Needs to be implemented by the user of the library.
+/// On construction a measurement should start, on destruction it should be stopped.
+class alignas(16) ExternalProfileMeasurement : public NonCopyable
+{	
+public:						
+	/// Constructor
+									ExternalProfileMeasurement(const char *inName, uint32 inColor = 0);
+									~ExternalProfileMeasurement();
+
+private:
+	uint8							mUserData[64];
+};
+
+} // JPH
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Macros to do the actual profiling	
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// Dummy implementations
+#define JPH_PROFILE_THREAD_START(name)			
+#define JPH_PROFILE_THREAD_END()				
+#define JPH_PROFILE_NEXTFRAME()			
+#define JPH_PROFILE_DUMP()				
+								
+// Scope profiling measurement
+#define JPH_PROFILE_TAG2(line)		profile##line
+#define JPH_PROFILE_TAG(line)		JPH_PROFILE_TAG2(line)
+
+/// Macro to collect profiling information.
+///
+/// Usage:
+///
+///		{
+///			JPH_PROFILE("Operation");
+///			do operation;
+///		}
+///
+#define JPH_PROFILE(...)			ExternalProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
+
+// Scope profiling for function
+#define JPH_PROFILE_FUNCTION()		JPH_PROFILE(JPH_FUNCTION_NAME)
+
+#elif defined(JPH_PROFILE_ENABLED)
+
+namespace JPH {
+
+class ProfileSample;
+class ProfileThread;
+
+/// Singleton class for managing profiling information
+class Profiler : public NonCopyable
+{
+public:
+	/// Increments the frame counter to provide statistics per frame
+	void						NextFrame();
+
+	/// Dump profiling statistics at the start of the next frame
+	void						Dump();
+
+	/// Add a thread to be instrumented
+	void						AddThread(ProfileThread *inThread);
+
+	/// Remove a thread from being instrumented
+	void						RemoveThread(ProfileThread *inThread);
+
+	/// Singleton instance
+	static Profiler				sInstance;
+								
+private:
+	/// Helper class to freeze ProfileSamples per thread while processing them
+	struct ThreadSamples
+	{
+		string					mThreadName;
+		ProfileSample *			mSamplesBegin;
+		ProfileSample *			mSamplesEnd;
+	};
+
+	/// Helper class to aggregate ProfileSamples
+	class Aggregator			
+	{	
+	public:						
+		/// Constructor
+								Aggregator(const char *inName)										: mName(inName) { }
+								
+		/// Accumulate results for a measurement
+		void					AccumulateMeasurement(uint64 inCyclesInCallWithChildren, uint64 inCyclesInChildren)
+		{
+			mCallCounter++;
+			mTotalCyclesInCallWithChildren += inCyclesInCallWithChildren;
+			mTotalCyclesInChildren += inCyclesInChildren;
+			mMinCyclesInCallWithChildren = min(inCyclesInCallWithChildren, mMinCyclesInCallWithChildren);
+			mMaxCyclesInCallWithChildren = max(inCyclesInCallWithChildren, mMaxCyclesInCallWithChildren);
+		}
+
+		/// Sort descending by total cycles
+		bool					operator < (const Aggregator &inRHS) const
+		{
+			return mTotalCyclesInCallWithChildren > inRHS.mTotalCyclesInCallWithChildren;
+		}
+
+		/// Identification			
+		const char *			mName;																///< User defined name of this item
+								
+		/// Statistics				
+		uint32					mCallCounter = 0;													///< Number of times AccumulateMeasurement was called
+		uint64					mTotalCyclesInCallWithChildren = 0;									///< Total amount of cycles spent in this scope
+		uint64					mTotalCyclesInChildren = 0;											///< Total amount of cycles spent in children of this scope
+		uint64					mMinCyclesInCallWithChildren = 0xffffffffffffffffUL;				///< Minimum amount of cycles spent per call
+		uint64					mMaxCyclesInCallWithChildren = 0;									///< Maximum amount of cycles spent per call
+	};							
+
+	using Threads = vector<ThreadSamples>;
+	using Aggregators = vector<Aggregator>;
+	using KeyToAggregator = unordered_map<const char *, size_t>;
+
+	/// Helper function to aggregate profile sample data
+	static void					sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator);
+
+	/// Dump profiling statistics
+	void						DumpInternal();
+	void						DumpList(int inNumber, const Aggregators &inAggregators);
+	void						DumpChart(int inNumber, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators);
+
+	mutex						mLock;																///< Lock that protects mThreads
+	vector<ProfileThread *>		mThreads;															///< List of all active threads
+	bool						mDump = false;														///< When true, the samples are dumped next frame
+};							
+
+// Class that contains the information of a single scoped measurement
+class alignas(16) ProfileSample : public NonCopyable
+{
+public:
+	const char *				mName;																///< User defined name of this item
+	uint32						mColor;																///< Color to use for this sample
+	uint8						mDepth;																///< Calculated depth
+	uint8						mUnused[3];
+	uint64						mStartCycle;														///< Cycle counter at start of measurement
+	uint64						mEndCycle;															///< Cycle counter at end of measurement
+};
+
+/// Collects all samples of a single thread
+class ProfileThread : public NonCopyable
+{
+public:
+	/// Constructor
+	inline						ProfileThread(const string &inThreadName);
+	inline						~ProfileThread();
+
+	static const uint cMaxSamples = 65536;
+
+	string						mThreadName;														///< Name of the thread that we're collecting information for
+	ProfileSample				mSamples[cMaxSamples];												///< Buffer of samples
+	uint						mCurrentSample = 0;													///< Next position to write a sample to
+
+	static thread_local ProfileThread *sInstance;
+};
+
+/// Create this class on the stack to start sampling timing information of a particular scope
+class ProfileMeasurement : public NonCopyable
+{	
+public:						
+	/// Constructor
+	inline						ProfileMeasurement(const char *inName, uint32 inColor = 0);
+	inline						~ProfileMeasurement();
+							
+private:
+	ProfileSample *				mSample;
+	ProfileSample				mTemp;
+
+	static bool					sOutOfSamplesReported;
+};
+
+} // JPH
+
+#include "Profiler.inl"
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Macros to do the actual profiling	
+//////////////////////////////////////////////////////////////////////////////////////////
+
+/// Start instrumenting a thread
+#define JPH_PROFILE_THREAD_START(name)	ProfileThread::sInstance = new ProfileThread(name)
+
+/// End instrumenting a thread
+#define JPH_PROFILE_THREAD_END()		do { delete ProfileThread::sInstance; ProfileThread::sInstance = nullptr; } while (false)
+								
+/// Scope profiling measurement
+#define JPH_PROFILE_TAG2(line)			profile##line
+#define JPH_PROFILE_TAG(line)			JPH_PROFILE_TAG2(line)
+#define JPH_PROFILE(...)				ProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
+
+/// Scope profiling for function
+#define JPH_PROFILE_FUNCTION()			JPH_PROFILE(JPH_FUNCTION_NAME)
+								
+/// Update frame counter								
+#define JPH_PROFILE_NEXTFRAME()			Profiler::sInstance.NextFrame()
+
+/// Dump profiling info
+#define JPH_PROFILE_DUMP()				Profiler::sInstance.Dump()
+
+#else
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Dummy profiling instructions
+//////////////////////////////////////////////////////////////////////////////////////////
+
+#define JPH_PROFILE_THREAD_START(name)
+#define JPH_PROFILE_THREAD_END()
+#define JPH_PROFILE(...)
+#define JPH_PROFILE_FUNCTION()
+#define JPH_PROFILE_NEXTFRAME()
+#define JPH_PROFILE_DUMP()
+
+#endif

+ 87 - 0
Jolt/Core/Profiler.inl

@@ -0,0 +1,87 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+namespace JPH {
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// ProfileThread
+//////////////////////////////////////////////////////////////////////////////////////////
+
+ProfileThread::ProfileThread(const string &inThreadName) :
+	mThreadName(inThreadName)
+{
+	Profiler::sInstance.AddThread(this);
+}
+
+ProfileThread::~ProfileThread()
+{
+	Profiler::sInstance.RemoveThread(this);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// ProfileMeasurement
+//////////////////////////////////////////////////////////////////////////////////////////
+
+ProfileMeasurement::ProfileMeasurement(const char *inName, uint32 inColor)	
+{
+	if (ProfileThread::sInstance == nullptr)
+	{
+		// Thread not instrumented
+		mSample = nullptr;
+	}
+	else if (ProfileThread::sInstance->mCurrentSample < ProfileThread::cMaxSamples)
+	{
+		// Get pointer to write data to
+		mSample = &ProfileThread::sInstance->mSamples[ProfileThread::sInstance->mCurrentSample++];
+
+		// Start constructing sample (will end up on stack)
+		mTemp.mName = inName;
+		mTemp.mColor = inColor;
+
+		// Collect start sample last
+		mTemp.mStartCycle = GetProcessorTickCount();
+	}
+	else
+	{
+		// Out of samples
+		if (!sOutOfSamplesReported)
+		{
+			Trace("ProfileMeasurement: Too many samples, some data will be lost!");
+			sOutOfSamplesReported = true;
+		}
+		mSample = nullptr;
+	}
+}
+
+ProfileMeasurement::~ProfileMeasurement()
+{
+	if (mSample != nullptr)
+	{
+		// Finalize sample
+		mTemp.mEndCycle = GetProcessorTickCount();
+
+		// Write it to the memory buffer bypassing the cache
+		static_assert(sizeof(ProfileSample) == 32, "Assume 32 bytes");
+		static_assert(alignof(ProfileSample) == 16, "Assume 16 byte alignment");
+	#if defined(JPH_USE_SSE)
+		const __m128i *src = reinterpret_cast<const __m128i *>(&mTemp);
+		__m128i *dst = reinterpret_cast<__m128i *>(mSample);
+		__m128i val = _mm_loadu_si128(src);
+		_mm_stream_si128(dst, val);
+		val = _mm_loadu_si128(src + 1);
+		_mm_stream_si128(dst + 1, val);
+	#elif defined(JPH_USE_NEON)
+		const int *src = reinterpret_cast<const int *>(&mTemp);
+		int *dst = reinterpret_cast<int *>(mSample);
+		int32x4_t val = vld1q_s32(src);
+		vst1q_s32(dst, val);
+		val = vld1q_s32(src + 4);
+		vst1q_s32(dst + 4, val);
+	#else
+		#error Unsupported CPU architecture
+	#endif
+		mSample = nullptr;
+	}
+}
+
+} // JPH

+ 155 - 0
Jolt/Core/RTTI.cpp

@@ -0,0 +1,155 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/RTTI.h>
+#include <Core/StringTools.h>
+
+namespace JPH {
+
+JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(RTTIAttribute)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// RTTI
+//////////////////////////////////////////////////////////////////////////////////////////
+
+RTTI::RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject) :
+	mName(inName),
+	mSize(inSize),
+	mCreate(inCreateObject),
+	mDestruct(inDestructObject)
+{
+	JPH_ASSERT(inDestructObject != nullptr, "Object cannot be destructed");
+}
+
+RTTI::RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject, pCreateRTTIFunction inCreateRTTI) :
+	mName(inName),
+	mSize(inSize),
+	mCreate(inCreateObject),
+	mDestruct(inDestructObject)
+{
+	JPH_ASSERT(inDestructObject != nullptr, "Object cannot be destructed");
+
+	inCreateRTTI(*this);
+}
+
+int RTTI::GetBaseClassCount() const
+{ 
+	return (int)mBaseClasses.size(); 
+}
+
+const RTTI *RTTI::GetBaseClass(int inIdx) const								
+{ 
+	return mBaseClasses[inIdx].mRTTI; 
+}
+
+uint32 RTTI::GetHash() const
+{ 
+	// Perform diffusion step to get from 64 to 32 bits (see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function)
+	uint64 hash = HashString(mName);
+	return (uint32)(hash ^ (hash >> 32));
+}
+
+void *RTTI::CreateObject() const
+{ 
+	return IsAbstract()? nullptr : mCreate(); 
+}
+
+void RTTI::DestructObject(void *inObject) const
+{ 
+	mDestruct(inObject); 
+}
+
+void RTTI::AddBaseClass(const RTTI *inRTTI, int inOffset)
+{ 
+	JPH_ASSERT(inOffset >= 0 && inOffset < mSize, "Base class not contained in derived class");
+
+	// Add base class
+	BaseClass base;
+	base.mRTTI = inRTTI;
+	base.mOffset = inOffset;
+	mBaseClasses.push_back(base); 
+
+	// Add attributes of base class
+	for (AttributeRefC a : inRTTI->mAttributes)
+		mAttributes.push_back(a);
+}
+
+bool RTTI::operator == (const RTTI &inRHS) const							
+{ 
+	// Compare addresses 
+	if (this == &inRHS) 
+		return true;
+
+	// Check that the names differ (if that is the case we probably have two instances
+	// of the same attribute info across the program, probably the second is in a DLL)
+	JPH_ASSERT(strcmp(mName, inRHS.mName) != 0);
+	return false;
+}
+
+bool RTTI::IsKindOf(const RTTI *inRTTI) const
+{
+	// Check if this is the same type
+	if (this == inRTTI)
+		return true;
+
+	// Check all base classes
+	for (const BaseClass &b : mBaseClasses)
+		if (b.mRTTI->IsKindOf(inRTTI))
+			return true;
+
+	return false;
+}
+
+const void *RTTI::CastTo(const void *inObject, const RTTI *inRTTI) const
+{
+	JPH_ASSERT(inObject != nullptr);
+	
+	// Check if this is the same type
+	if (this == inRTTI)
+		return inObject;
+
+	// Check all base classes
+	for (const BaseClass &b : mBaseClasses)
+	{
+		// Cast the pointer to the base class
+		const void *casted = (const void *)(((const uint8 *)inObject) + b.mOffset);
+
+		// Test base class
+		const void *rv = b.mRTTI->CastTo(casted, inRTTI);
+		if (rv != nullptr)
+			return rv;
+	}
+
+	// Not possible to cast
+	return nullptr;
+}
+
+void RTTI::AddAttribute(RTTIAttribute *inAttribute)
+{ 
+	mAttributes.push_back(inAttribute); 
+}
+
+int RTTI::GetAttributeCount() const									
+{ 
+	return (int)mAttributes.size(); 
+}
+
+const RTTIAttribute *RTTI::GetAttribute(int inIdx) const
+{ 
+	return mAttributes[inIdx]; 
+}
+
+const RTTIAttribute *RTTI::GetAttribute(const RTTI *inRTTI, const char *inName) const
+{
+	for (AttributeRefC a : mAttributes)
+		if (::JPH::IsKindOf(a, inRTTI) && strcmp(inName, a->GetName()) == 0) 
+			return a;
+
+	return nullptr;
+}
+
+} // JPH

+ 467 - 0
Jolt/Core/RTTI.h

@@ -0,0 +1,467 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/Reference.h>
+#include <Core/StaticArray.h>
+
+namespace JPH {
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// RTTI
+//////////////////////////////////////////////////////////////////////////////////////////
+
+class RTTIAttribute;
+
+/// Light weight runtime type information system. This way we don't need to turn
+/// on the default RTTI system of the compiler (introducing a possible overhead for every
+/// class)
+///
+/// Notes:
+///  - An extra virtual member function is added. This adds 8 bytes to the size of 
+///    an instance of the class (unless you are already using virtual functions).
+///
+///
+/// To use RTTI on a specific class use:
+///
+/// Header file:
+///
+///		class Foo
+///		{
+///			JPH_DECLARE_RTTI_VIRTUAL_BASE(Foo)
+///		}
+///
+///		class Bar : public Foo
+///		{
+///			JPH_DECLARE_RTTI_VIRTUAL(Bar)
+///		};
+///
+/// Implementation file:
+///
+///		JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(Foo)
+///		{
+///		}
+///
+///		JPH_IMPLEMENT_RTTI_VIRTUAL(Bar)
+///		{ 
+///			JPH_ADD_BASE_CLASS(Bar, Foo) // Multiple inheritance is allowed, just do JPH_ADD_BASE_CLASS for every base class
+///		}
+///
+/// For abstract classes use:
+///
+/// Header file:
+///
+///		class Foo
+///		{
+///			JPH_DECLARE_RTTI_ABSTRACT_BASE(Foo)
+///
+///		public:
+///			virtual void AbstractFunction() = 0;
+///		}
+///
+///		class Bar : public Foo
+///		{
+///			JPH_DECLARE_RTTI_VIRTUAL(Bar)
+///
+///		public:
+///			virtual void AbstractFunction() { } // Function is now implemented so this class is no longer abstract
+///		};
+///
+/// Implementation file:
+///
+///		JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(Foo)
+///		{
+///		}
+///
+///		JPH_IMPLEMENT_RTTI_VIRTUAL(Bar)
+///		{ 
+///			JPH_ADD_BASE_CLASS(Bar, Foo)
+///		}
+///
+/// Example of usage in a program:
+///
+///		pFoo foo_ptr = new Foo;
+///		pFoo bar_ptr = new Bar;
+///
+///		IsType(foo_ptr, RTTI(Bar)) returns false
+///		IsType(bar_ptr, RTTI(Bar)) returns true
+///
+///		IsKindOf(foo_ptr, RTTI(Bar)) returns false
+///		IsKindOf(bar_ptr, RTTI(Foo)) returns true
+///		IsKindOf(bar_ptr, RTTI(Bar)) returns true
+///
+///		StaticCast<Bar>(foo_ptr) asserts and returns foo_ptr casted to pBar
+///		StaticCast<Bar>(bar_ptr) returns bar_ptr casted to pBar
+///
+///		DynamicCast<Bar>(foo_ptr) returns nullptr
+///		DynamicCast<Bar>(bar_ptr) returns bar_ptr casted to pBar
+///
+/// Other feature of DynamicCast:
+/// 
+///		class A { int data[5]; };
+///		class B { int data[7]; };
+///		class C : public A, public B { int data[9]; };
+///		
+///		C *c = new C;
+///		A *a = c;
+/// 
+/// Note that:
+///
+///		B *b = (B *)a;
+///
+/// generates an invalid pointer,
+///
+///		B *b = StaticCast<B>(a);
+///
+/// doesn't compile, and
+///
+///		B *b = DynamicCast<B>(a);
+/// 
+/// does the correct cast
+class RTTI
+{
+public:
+	/// Function to create an object
+	using pCreateObjectFunction = void *(*)();
+
+	/// Function to destroy an object
+	using pDestructObjectFunction = void (*)(void *inObject);
+
+	/// Function to initialize the runtime type info structure
+	using pCreateRTTIFunction = void (*)(RTTI &inRTTI);
+
+	/// Constructor
+								RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject);
+								RTTI(const char *inName, int inSize, pCreateObjectFunction inCreateObject, pDestructObjectFunction inDestructObject, pCreateRTTIFunction inCreateRTTI);
+
+	// Properties
+	inline const char *			GetName() const												{ return mName; }
+	void						SetName(const char *inName)									{ mName = inName; }
+	inline int					GetSize() const												{ return mSize; }
+	bool						IsAbstract() const											{ return mCreate == nullptr || mDestruct == nullptr; }
+	int							GetBaseClassCount() const;
+	const RTTI *				GetBaseClass(int inIdx) const;
+	uint32						GetHash() const;
+
+	/// Create an object of this type (returns nullptr if the object is abstract)
+	void *						CreateObject() const;
+
+	/// Destruct object of this type (does nothing if the object is abstract)
+	void						DestructObject(void *inObject) const;
+
+	/// Add base class
+	void						AddBaseClass(const RTTI *inRTTI, int inOffset);
+	
+	/// Equality operators
+	bool						operator == (const RTTI &inRHS) const;
+	bool						operator != (const RTTI &inRHS) const						{ return !(*this == inRHS); }
+
+	/// Test if this class is derived from class of type inRTTI
+	bool						IsKindOf(const RTTI *inRTTI) const;
+
+	/// Cast inObject of this type to object of type inRTTI, returns nullptr if the cast is unsuccessful
+	const void *				CastTo(const void *inObject, const RTTI *inRTTI) const;
+
+	/// Attribute access
+	void						AddAttribute(RTTIAttribute *inAttribute);
+	int							GetAttributeCount() const;
+	const RTTIAttribute *		GetAttribute(int inIdx) const;
+	const RTTIAttribute *		GetAttribute(const RTTI *inRTTI, const char *inName) const;
+
+protected:
+	/// Base class information
+	struct BaseClass
+	{
+		const RTTI *			mRTTI;
+		int						mOffset;
+	};
+
+	using AttributeRefC = RefConst<RTTIAttribute>;
+
+	const char *				mName;														///< Class name
+	int							mSize;														///< Class size
+	vector<BaseClass>			mBaseClasses;												///< Names of base classes
+	pCreateObjectFunction		mCreate;													///< Pointer to a function that will create a new instance of this class
+	pDestructObjectFunction		mDestruct;													///< Pointer to a function that will destruct an object of this class
+	vector<AttributeRefC>		mAttributes;												///< All attributes of this class
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Add run time type info to types that don't have virtual functions
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// JPH_DECLARE_RTTI_NON_VIRTUAL
+#define JPH_DECLARE_RTTI_NON_VIRTUAL(class_name)																	\
+public:																												\
+	friend RTTI *				GetRTTIOfType(class_name *);														\
+	friend inline const RTTI *	GetRTTI(const class_name *inObject) { return GetRTTIOfType((class_name *)nullptr); }\
+	static void					sCreateRTTI(RTTI &inRTTI);															\
+
+// JPH_IMPLEMENT_RTTI_NON_VIRTUAL
+#define JPH_IMPLEMENT_RTTI_NON_VIRTUAL(class_name)																	\
+	RTTI *						GetRTTIOfType(class_name *)															\
+	{																												\
+		static RTTI rtti(#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \
+		return &rtti;																								\
+	}																												\
+	void						class_name::sCreateRTTI(RTTI &inRTTI)												\
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Same as above, but when you cannot insert the declaration in the class
+// itself, for example for templates and third party classes
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// JPH_DECLARE_RTTI_OUTSIDE_CLASS
+#define JPH_DECLARE_RTTI_OUTSIDE_CLASS(class_name)																	\
+	RTTI *						GetRTTIOfType(class_name *);														\
+	inline const RTTI *			GetRTTI(const class_name *inObject) { return GetRTTIOfType((class_name *)nullptr); }\
+	void						CreateRTTI##class_name(RTTI &inRTTI);												\
+
+// JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS
+#define JPH_IMPLEMENT_RTTI_OUTSIDE_CLASS(class_name)																\
+	RTTI *						GetRTTIOfType(class_name *)															\
+	{																												\
+		static RTTI rtti((const char *)#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &CreateRTTI##class_name); \
+		return &rtti;																								\
+	}																												\
+	void						CreateRTTI##class_name(RTTI &inRTTI)
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Same as above, but for classes that have virtual functions
+//////////////////////////////////////////////////////////////////////////////////////////
+
+#define JPH_DECLARE_RTTI_HELPER(class_name, modifier)																\
+public:																												\
+	friend RTTI *				GetRTTIOfType(class_name *);														\
+	friend inline const RTTI *	GetRTTI(const class_name *inObject) { return inObject->GetRTTI(); }					\
+	virtual const RTTI *		GetRTTI() const modifier;															\
+	virtual const void *		CastTo(const RTTI *inRTTI) const modifier;											\
+	static void					sCreateRTTI(RTTI &inRTTI);															\
+
+// JPH_DECLARE_RTTI_VIRTUAL - for derived classes with RTTI
+#define JPH_DECLARE_RTTI_VIRTUAL(class_name)																		\
+	JPH_DECLARE_RTTI_HELPER(class_name, override)
+
+// JPH_IMPLEMENT_RTTI_VIRTUAL
+#define JPH_IMPLEMENT_RTTI_VIRTUAL(class_name)																		\
+	RTTI *						GetRTTIOfType(class_name *)															\
+	{																												\
+		static RTTI rtti(#class_name, sizeof(class_name), []() -> void * { return new class_name; }, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \
+		return &rtti;																								\
+	}																												\
+	const RTTI *				class_name::GetRTTI() const															\
+	{																												\
+		return JPH_RTTI(class_name);																				\
+	}																												\
+	const void *				class_name::CastTo(const RTTI *inRTTI) const										\
+	{																												\
+		return JPH_RTTI(class_name)->CastTo((const void *)this, inRTTI);											\
+	}																												\
+	void						class_name::sCreateRTTI(RTTI &inRTTI)												\
+
+// JPH_DECLARE_RTTI_VIRTUAL_BASE - for concrete base class that has RTTI
+#define JPH_DECLARE_RTTI_VIRTUAL_BASE(class_name)																	\
+	JPH_DECLARE_RTTI_HELPER(class_name, )
+
+// JPH_IMPLEMENT_RTTI_VIRTUAL_BASE
+#define JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(class_name)																	\
+	JPH_IMPLEMENT_RTTI_VIRTUAL(class_name)
+
+// JPH_DECLARE_RTTI_ABSTRACT - for derived abstract class that have RTTI
+#define JPH_DECLARE_RTTI_ABSTRACT(class_name)																		\
+	JPH_DECLARE_RTTI_HELPER(class_name, override)
+
+// JPH_IMPLEMENT_RTTI_ABSTRACT
+#define JPH_IMPLEMENT_RTTI_ABSTRACT(class_name)																		\
+	RTTI *						GetRTTIOfType(class_name *)															\
+	{																												\
+		static RTTI rtti(#class_name, sizeof(class_name), nullptr, [](void *inObject) { delete (class_name *)inObject; }, &class_name::sCreateRTTI); \
+		return &rtti;																								\
+	}																												\
+	const RTTI *				class_name::GetRTTI() const															\
+	{																												\
+		return JPH_RTTI(class_name);																				\
+	}																												\
+	const void *				class_name::CastTo(const RTTI *inRTTI) const										\
+	{																												\
+		return JPH_RTTI(class_name)->CastTo((const void *)this, inRTTI);											\
+	}																												\
+	void						class_name::sCreateRTTI(RTTI &inRTTI)												\
+
+// JPH_DECLARE_RTTI_ABSTRACT_BASE - for abstract base class that has RTTI
+#define JPH_DECLARE_RTTI_ABSTRACT_BASE(class_name)																	\
+	JPH_DECLARE_RTTI_HELPER(class_name, )
+
+// JPH_IMPLEMENT_RTTI_ABSTRACT_BASE
+#define JPH_IMPLEMENT_RTTI_ABSTRACT_BASE(class_name)																\
+	JPH_IMPLEMENT_RTTI_ABSTRACT(class_name)
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Declare an RTTI class for registering with the factory
+//////////////////////////////////////////////////////////////////////////////////////////
+
+#define JPH_DECLARE_RTTI_FOR_FACTORY(class_name)																	\
+	RTTI *						GetRTTIOfType(class class_name *);
+
+#define JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(name_space, class_name)											\
+	namespace name_space {																							\
+		class class_name; 																							\
+		RTTI *					GetRTTIOfType(class class_name *);													\
+	}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Find the RTTI of a class
+//////////////////////////////////////////////////////////////////////////////////////////
+
+#define JPH_RTTI(class_name)	GetRTTIOfType((class_name *)nullptr)
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Macro to rename a class, useful for embedded classes:
+//
+// class A { class B { }; }
+//
+// Now use JPH_RENAME_CLASS(B, A::B) to avoid conflicts with other classes named B
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// JPH_RENAME_CLASS
+#define JPH_RENAME_CLASS(class_name, new_name)																		\
+								inRTTI.SetName(#new_name);
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Macro to add base classes
+//////////////////////////////////////////////////////////////////////////////////////////
+
+/// Define very dirty macro to get the offset of a baseclass into a class
+#define JPH_BASE_CLASS_OFFSET(inClass, inBaseClass)	((int(uint64((inBaseClass *)((inClass *)0x10000))))-0x10000)
+
+// JPH_ADD_BASE_CLASS 
+#define JPH_ADD_BASE_CLASS(class_name, base_class_name)																\
+								inRTTI.AddBaseClass(JPH_RTTI(base_class_name), JPH_BASE_CLASS_OFFSET(class_name, base_class_name));
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Macros and templates to identify a class
+//////////////////////////////////////////////////////////////////////////////////////////
+
+/// Check if inObject is of DstType
+template <class Type>
+inline bool IsType(const Type *inObject, const RTTI *inRTTI)
+{
+	return inObject == nullptr || *inObject->GetRTTI() == *inRTTI;
+}
+
+template <class Type>
+inline bool IsType(const RefConst<Type> &inObject, const RTTI *inRTTI)
+{
+	return inObject == nullptr || *inObject->GetRTTI() == *inRTTI;
+}
+
+template <class Type>
+inline bool IsType(const Ref<Type> &inObject, const RTTI *inRTTI)
+{
+	return inObject == nullptr || *inObject->GetRTTI() == *inRTTI;
+}
+
+/// Check if inObject is or is derived from DstType
+template <class Type>
+inline bool IsKindOf(const Type *inObject, const RTTI *inRTTI)
+{
+	return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI);
+}
+
+template <class Type>
+inline bool IsKindOf(const RefConst<Type> &inObject, const RTTI *inRTTI)
+{
+	return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI);
+}
+
+template <class Type>
+inline bool IsKindOf(const Ref<Type> &inObject, const RTTI *inRTTI)
+{
+	return inObject == nullptr || inObject->GetRTTI()->IsKindOf(inRTTI);
+}
+
+/// Cast inObject to DstType, asserts on failure
+template <class DstType, class SrcType>
+inline const DstType *StaticCast(const SrcType *inObject)
+{
+	JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast");
+	return static_cast<const DstType *>(inObject);
+}
+
+template <class DstType, class SrcType>
+inline DstType *StaticCast(SrcType *inObject)
+{
+	JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast");
+	return static_cast<DstType *>(inObject);
+}
+
+template <class DstType, class SrcType>
+inline RefConst<DstType> StaticCast(RefConst<SrcType> &inObject)
+{
+	JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast");
+	return static_cast<const DstType *>(inObject.GetPtr());
+}
+
+template <class DstType, class SrcType>
+inline Ref<DstType> StaticCast(Ref<SrcType> &inObject)
+{
+	JPH_ASSERT(IsKindOf(inObject, JPH_RTTI(DstType)), "Invalid cast");
+	return static_cast<DstType *>(inObject.GetPtr());
+}
+
+/// Cast inObject to DstType, returns nullptr on failure
+template <class DstType, class SrcType>
+inline const DstType *DynamicCast(const SrcType *inObject)
+{
+	return inObject != nullptr? reinterpret_cast<const DstType *>(inObject->CastTo(JPH_RTTI(DstType))) : nullptr;
+}
+
+template <class DstType, class SrcType>
+inline DstType *DynamicCast(SrcType *inObject)
+{
+	return inObject != nullptr? const_cast<DstType *>(reinterpret_cast<const DstType *>(inObject->CastTo(JPH_RTTI(DstType)))) : nullptr;
+}
+
+template <class DstType, class SrcType>
+inline RefConst<DstType> DynamicCast(RefConst<SrcType> &inObject)
+{
+	return inObject != nullptr? reinterpret_cast<const DstType *>(inObject->CastTo(JPH_RTTI(DstType))) : nullptr;
+}
+
+template <class DstType, class SrcType>
+inline Ref<DstType> DynamicCast(Ref<SrcType> &inObject)
+{
+	return inObject != nullptr? const_cast<DstType *>(reinterpret_cast<const DstType *>(inObject->CastTo(JPH_RTTI(DstType)))) : nullptr;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// RTTIAttribute
+//////////////////////////////////////////////////////////////////////////////////////////
+
+/// Represents a member of a class.
+class RTTIAttribute : public RefTarget<RTTIAttribute>
+{
+	JPH_DECLARE_RTTI_VIRTUAL_BASE(RTTIAttribute)
+
+public:
+	/// Constructor
+								RTTIAttribute()												: mName("") { }
+								RTTIAttribute(const char *inName)							: mName(inName) { }
+	virtual						~RTTIAttribute()											{ }
+
+	/// Name of the attribute
+	void						SetName(const char *inName)									{ mName = inName; }
+	const char *				GetName() const												{ return mName; }
+
+	/// In case this attribute contains an RTTI type, return it (note that a vector<sometype> will return the rtti of sometype)
+	virtual const RTTI *		GetMemberPrimitiveType() const								{ return nullptr; }
+
+	/// In case this attribute references an object, return data pointer here (note that if the attribute is vector<sometype> it will return the address of the vector)
+	virtual const void *		GetMemberPointer(const void *inObject) const				{ return nullptr; }
+
+private:
+	const char *				mName;
+};
+
+} // JPH

+ 212 - 0
Jolt/Core/Reference.h

@@ -0,0 +1,212 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <atomic>
+
+namespace JPH {
+
+// Forward declares
+template <class T> class Ref;
+template <class T> class RefConst;
+
+/// Simple class to facilitate reference counting / releasing
+/// Derive your class from RefTarget and you can reference it by using Ref<classname> or RefConst<classname>
+///
+/// Reference counting classes keep an integer which indicates how many references
+/// to the object are active. Reference counting objects are derived from RefTarget
+/// and staT & their life with a reference count of zero. They can then be assigned
+/// to equivalents of pointers (Ref) which will increase the reference count immediately.
+/// If the destructor of Ref is called or another object is assigned to the reference
+/// counting pointer it will decrease the reference count of the object again. If this
+/// reference count becomes zero, the object is destroyed.
+///
+/// This provides a very powerful mechanism to prevent memory leaks, but also gives
+/// some responsibility to the programmer. The most notable point is that you cannot
+/// have one object reference another and have the other reference the first one
+/// back, because this way the reference count of both objects will never become
+/// lower than 1, resulting in a memory leak. By carefully designing your classses
+/// (and particularly identifying who owns who in the class hierarchy) you can avoid
+/// these problems.
+template <class T>
+class RefTarget		
+{	
+public:
+	/// Constructor
+	inline					RefTarget()										: mRefCount(0) { }
+	inline					RefTarget(const RefTarget &)					: mRefCount(0) { }
+	inline					~RefTarget()									{ JPH_ASSERT(mRefCount == 0 || mRefCount == cEmbedded); } ///< assert no one is referencing us
+
+	/// Mark this class as embedded, this means the type can be used in a compound or constructed on the stack.
+	/// The Release function will never destruct the object, it is assumed the destructor will be called by whoever allocated
+	/// the object and at that point in time it is checked that no references are left to the structure.
+	inline void				SetEmbedded()									{ JPH_ASSERT(mRefCount < cEmbedded); mRefCount += cEmbedded; }
+
+	/// Assignment operator
+	inline RefTarget &		operator = (const RefTarget &inRHS)				{ return *this; }
+
+	/// Get current refcount of this object
+	uint32					GetRefCount() const								{ return mRefCount; }
+
+	/// Add or release a reference to this object
+	inline void				AddRef() const									{ ++mRefCount; }
+	inline void				Release() const									{ if (--mRefCount == 0) delete static_cast<const T *>(this); }
+
+	/// INTERNAL HELPER FUNCTION USED BY SERIALIZATION
+	static int				sInternalGetRefCountOffset()					{ return offsetof(T, mRefCount); }
+
+	/// INTERNAL HELPER FUNCTION TO OVERRIDE THE REFCOUNT OF AN OBJECT. USE WITH GREAT CARE! 
+	inline void				SetRefCountInternal(uint32 inRefCount)			{ JPH_ASSERT(inRefCount >= 0); mRefCount = inRefCount; }
+
+protected:
+	static constexpr uint32 cEmbedded = 0x0ebedded;							///< A large value that gets added to the refcount to mark the object as embedded
+
+	mutable atomic<uint32>	mRefCount;										///< Current reference count
+};							
+
+/// Pure virtual version of RefTarget
+class RefTargetVirtual
+{
+public:
+	/// Virtual destructor
+	virtual					~RefTargetVirtual()								{ }
+
+	/// Virtual add reference
+	virtual void			AddRef() = 0;
+
+	/// Virtual release reference
+	virtual void			Release() = 0;
+};
+
+/// Class for automatic referencing, this is the equivalent of a pointer to type T
+/// if you assign a value to this class it will increment the reference count by one
+/// of this object, and if you assign something else it will decrease the reference
+/// count of the first object again. If it reaches a reference count of zero it will
+/// be deleted
+template <class T>
+class Ref
+{
+public:
+	/// Constructor
+	inline					Ref()											: mPtr(nullptr) { }
+	inline					Ref(T *inRHS)									: mPtr(inRHS) { AddRef(); }
+	inline					Ref(const Ref<T> &inRHS)						: mPtr(inRHS.mPtr) { AddRef(); }
+	inline					Ref(Ref<T> &&inRHS)								: mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }
+	inline					~Ref()											{ Release(); }
+						
+	/// Assignment operators
+	inline Ref<T> &			operator = (T *inRHS) 							{ if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }
+	inline Ref<T> &			operator = (const Ref<T> &inRHS)				{ if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
+	inline Ref<T> &			operator = (Ref<T> &&inRHS)						{ if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }
+						
+	/// Casting operators
+	inline					operator T * const () const						{ return mPtr; }
+	inline					operator T *()									{ return mPtr; }
+						
+	/// Access like a normal pointer
+	inline T * const 		operator -> () const							{ return mPtr; }
+	inline T *				operator -> ()									{ return mPtr; }
+	inline T &				operator * () const								{ return *mPtr; }
+
+	/// Comparison
+	inline bool				operator == (const T * inRHS) const				{ return mPtr == inRHS; }
+	inline bool				operator == (const Ref<T> &inRHS) const			{ return mPtr == inRHS.mPtr; }
+	inline bool				operator != (const T * inRHS) const				{ return mPtr != inRHS; }
+	inline bool				operator != (const Ref<T> &inRHS) const			{ return mPtr != inRHS.mPtr; }
+
+	/// Get pointer
+	inline T * 				GetPtr() const									{ return mPtr; }
+	inline T *				GetPtr()										{ return mPtr; }
+
+	/// INTERNAL HELPER FUNCTION USED BY SERIALIZATION
+	void **					InternalGetPointer()							{ return reinterpret_cast<void **>(&mPtr); }
+
+private:
+	template <class T2> friend class RefConst;
+
+	/// Use "variable = nullptr;" to release an object, do not call these functions
+	inline void				AddRef()										{ if (mPtr != nullptr) mPtr->AddRef(); }
+	inline void				Release()										{ if (mPtr != nullptr) mPtr->Release(); }
+	
+	T *						mPtr;											///< Pointer to object that we are reference counting
+};						
+
+/// Class for automatic referencing, this is the equivalent of a CONST pointer to type T
+/// if you assign a value to this class it will increment the reference count by one
+/// of this object, and if you assign something else it will decrease the reference
+/// count of the first object again. If it reaches a reference count of zero it will
+/// be deleted
+template <class T>
+class RefConst
+{
+public:
+	/// Constructor
+	inline					RefConst()										: mPtr(nullptr) { }
+	inline					RefConst(const T * inRHS)						: mPtr(inRHS) { AddRef(); }
+	inline					RefConst(const RefConst<T> &inRHS)				: mPtr(inRHS.mPtr) { AddRef(); }
+	inline					RefConst(RefConst<T> &&inRHS)					: mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }
+	inline					RefConst(const Ref<T> &inRHS)					: mPtr(inRHS.mPtr) { AddRef(); }
+	inline					RefConst(Ref<T> &&inRHS)						: mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }
+	inline					~RefConst()										{ Release(); }
+						
+	/// Assignment operators
+	inline RefConst<T> &	operator = (const T * inRHS) 					{ if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }
+	inline RefConst<T> &	operator = (const RefConst<T> &inRHS)			{ if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
+	inline RefConst<T> &	operator = (RefConst<T> &&inRHS)				{ if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }
+	inline RefConst<T> &	operator = (const Ref<T> &inRHS)				{ if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
+	inline RefConst<T> &	operator = (Ref<T> &&inRHS)						{ if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }
+						
+	/// Casting operators
+	inline					operator const T * () const						{ return mPtr; }
+						
+	/// Access like a normal pointer
+	inline const T * 	 	operator -> () const							{ return mPtr; }
+	inline const T &		operator * () const								{ return *mPtr; }
+
+	/// Comparison
+	inline bool				operator == (const T * inRHS) const				{ return mPtr == inRHS; }
+	inline bool				operator == (const RefConst<T> &inRHS) const	{ return mPtr == inRHS.mPtr; }
+	inline bool				operator == (const Ref<T> &inRHS) const			{ return mPtr == inRHS.mPtr; }
+	inline bool				operator != (const T * inRHS) const				{ return mPtr != inRHS; }
+	inline bool				operator != (const RefConst<T> &inRHS) const	{ return mPtr != inRHS.mPtr; }
+	inline bool				operator != (const Ref<T> &inRHS) const			{ return mPtr != inRHS.mPtr; }
+
+	/// Get pointer
+	inline const T * 		GetPtr() const									{ return mPtr; }
+
+	/// INTERNAL HELPER FUNCTION USED BY SERIALIZATION
+	void **					InternalGetPointer()							{ return const_cast<void **>(reinterpret_cast<const void **>(&mPtr)); }
+
+private:
+	/// Use "variable = nullptr;" to release an object, do not call these functions
+	inline void				AddRef()										{ if (mPtr != nullptr) mPtr->AddRef(); }
+	inline void				Release()										{ if (mPtr != nullptr) mPtr->Release(); }
+	
+	const T *				mPtr;											///< Pointer to object that we are reference counting
+};						
+
+} // JPH
+
+namespace std
+{
+	/// Declare std::hash for Ref
+	template <class T> 
+	struct hash<JPH::Ref<T>>
+	{
+		size_t operator () (const JPH::Ref<T> &inRHS) const
+		{
+			return hash<T *> { }(inRHS.GetPtr());
+		}
+	};
+
+	/// Declare std::hash for RefConst
+	template <class T> 
+	struct hash<JPH::RefConst<T>>
+	{
+		size_t operator () (const JPH::RefConst<T> &inRHS) const
+		{
+			return hash<const T *> { }(inRHS.GetPtr());
+		}
+	};
+}

+ 175 - 0
Jolt/Core/Result.h

@@ -0,0 +1,175 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// Helper class that either contains a valid result or an error
+template <class Type>
+class Result
+{
+public:
+	/// Default constructor
+						Result()									{ }
+						
+	/// Copy constructor
+						Result(const Result<Type> &inRHS)
+	{
+		mState = inRHS.mState;
+
+		switch (inRHS.mState)
+		{
+		case EState::Valid:
+			new (&mResult) Type (inRHS.mResult);
+			break;
+
+		case EState::Error:
+			new (&mError) string(inRHS.mError);
+			break;
+
+		case EState::Invalid:
+			break;
+		}
+	}
+
+	/// Move constructor
+						Result(Result<Type> &&inRHS)
+	{
+		mState = inRHS.mState;
+
+		switch (inRHS.mState)
+		{
+		case EState::Valid:
+			new (&mResult) Type (move(inRHS.mResult));
+			break;
+
+		case EState::Error:
+			new (&mError) string(move(inRHS.mError));
+			break;
+
+		case EState::Invalid:
+			break;
+		}
+
+		inRHS.mState = EState::Invalid;
+	}
+
+	/// Destructor
+						~Result()									{ Clear(); }
+
+	/// Copy assignment
+	Result<Type> &		operator = (const Result<Type> &inRHS)
+	{
+		Clear();
+
+		mState = inRHS.mState;
+
+		switch (inRHS.mState)
+		{
+		case EState::Valid:
+			new (&mResult) Type (inRHS.mResult);
+			break;
+
+		case EState::Error:
+			new (&mError) string(inRHS.mError);
+			break;
+
+		case EState::Invalid:
+			break;
+		}
+
+		return *this;
+	}
+
+	/// Move assignment
+	Result<Type> &		operator = (Result<Type> &&inRHS)
+	{
+		Clear();
+
+		mState = inRHS.mState;
+
+		switch (inRHS.mState)
+		{
+		case EState::Valid:
+			new (&mResult) Type (move(inRHS.mResult));
+			break;
+
+		case EState::Error:
+			new (&mError) string(move(inRHS.mError));
+			break;
+
+		case EState::Invalid:
+			break;
+		}
+
+		inRHS.mState = EState::Invalid;
+
+		return *this;
+	}
+
+	/// Clear result or error
+	void				Clear()
+	{ 
+		switch (mState) 
+		{ 
+		case EState::Valid: 
+			mResult.~Type(); 
+			break; 
+
+		case EState::Error:
+			mError.~string();
+			break;
+
+		case EState::Invalid:
+			break;
+		}
+
+		mState = EState::Invalid;
+	}
+
+	/// Checks if the result is still uninitialized
+	bool				IsEmpty() const								{ return mState == EState::Invalid; }
+
+	/// Checks if the result is valid
+	bool				IsValid() const								{ return mState == EState::Valid; }
+
+	/// Get the result value
+	const Type &		Get() const									{ JPH_ASSERT(IsValid()); return mResult; }
+
+	/// Set the result value
+	void				Set(const Type &inResult)					{ Clear(); new (&mResult) Type(inResult); mState = EState::Valid; }
+
+	/// Set the result value (move value)
+	void				Set(const Type &&inResult)					{ Clear(); new (&mResult) Type(move(inResult)); mState = EState::Valid; }
+
+	/// Check if we had an error
+	bool				HasError() const							{ return mState == EState::Error; }
+
+	/// Get the error value
+	const string &		GetError() const							{ JPH_ASSERT(HasError()); return mError; }
+
+	/// Set an error value
+	void				SetError(const char *inError)				{ Clear(); new (&mError) string(inError); mState = EState::Error; }
+	void				SetError(const string &inError)				{ Clear(); new (&mError) string(inError); mState = EState::Error; }
+	void				SetError(string &&inError)					{ Clear(); new (&mError) string(move(inError)); mState = EState::Error; }
+
+private:
+	union
+	{
+		Type			mResult;									///< The actual result object
+		string			mError;										///< The error description if the result failed
+	};
+
+	/// State of the result
+	enum class EState : uint8
+	{
+		Invalid,
+		Valid,
+		Error
+	};
+
+	EState				mState = EState::Invalid;
+};
+
+} // JPH

+ 369 - 0
Jolt/Core/StatCollector.cpp

@@ -0,0 +1,369 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/StatCollector.h>
+#include <Core/Color.h>
+#include <Core/StringTools.h>
+#include <fstream>
+
+namespace JPH {
+
+#ifdef JPH_STAT_COLLECTOR
+
+StatCollector StatCollector::sInstance;
+
+string StatCollector::Variant::ToString() const
+{
+	switch (mType)
+	{
+	case EType::Float:
+		return ConvertToString(mFloat);
+	case EType::Int:
+		return ConvertToString(mInt);
+	case EType::Bool:
+		return ConvertToString(mBool);
+	case EType::Undefined:
+	default:
+		JPH_ASSERT(false);
+		return "";
+	}
+}
+
+void StatCollector::SetNextFrame()
+{
+	lock_guard<Mutex> lock(mMutex);
+
+	if (mIsCapturing)
+		mCurrentFrame = &mFrames[mCurrentFrameNumber++];
+}
+
+void StatCollector::ResetInternal()
+{
+	mCurrentFrameNumber = 0;
+	mCurrentFrame = nullptr;
+
+	mFrames.clear();
+	mKeys.clear();
+	mNextKey = 0;
+}
+
+void StatCollector::Reset()
+{
+	lock_guard<Mutex> lock(mMutex);
+
+	ResetInternal();	
+}
+
+void StatCollector::StartCapture()
+{
+	lock_guard<Mutex> lock(mMutex);
+
+	ResetInternal();
+
+	mIsCapturing = true;
+}
+
+void StatCollector::AddItem(const string &inName, const Variant &inValue)
+{
+	lock_guard<Mutex> lock(mMutex);
+
+	JPH_ASSERT(mCurrentFrame != nullptr, "Don't forget to call SetFrame(...)");
+
+	// Determine key for inName
+	pair<KeyIDMap::iterator, bool> p = mKeys.insert(KeyIDMap::value_type(inName, mNextKey));
+
+	// Increment key inName was new
+	if (p.second)
+		++mNextKey;
+
+	// Take key from map
+	int key = p.first->second;
+
+	// Store value
+	(*mCurrentFrame)[key] = inValue;
+}
+
+void StatCollector::AddItem(const string &inName, Vec3Arg inValue)
+{
+	AddItem(inName + ".X", inValue.GetX());
+	AddItem(inName + ".Y", inValue.GetY());
+	AddItem(inName + ".Z", inValue.GetZ());
+}
+
+void StatCollector::AddItem(const string &inName, QuatArg inValue)
+{
+	Vec3 axis;
+	float angle;
+	inValue.GetAxisAngle(axis, angle);
+	AddItem(inName + ".Axis", axis);
+	AddItem(inName + ".Angle", RadiansToDegrees(angle));
+}
+
+struct StatTreeNode
+{
+	using Children = map<string, StatTreeNode>;
+
+	int			mIndex = -1;
+	Children	mChildren;
+};
+
+static void sWriteStatTree(ofstream &ioStream, const StatTreeNode &inNode)
+{
+	bool first = true;
+	for (const StatTreeNode::Children::value_type &c : inNode.mChildren)
+	{
+		// Write comma if this is not the first line
+		if (!first)
+			ioStream << ",";
+		first = false;
+
+		// Write title
+		ioStream << "{title:\"" + c.first + "\"";
+		
+		// Write key
+		ioStream << ",key:\"" + ConvertToString(c.second.mIndex) + "\"";
+		
+		// Write children
+		if (!c.second.mChildren.empty())
+		{
+			ioStream << ",children:[";
+				sWriteStatTree(ioStream, c.second);
+			ioStream << "]";
+		}
+
+		ioStream << "}";
+	}
+}
+
+void StatCollector::StopCapture(const char *inFileName)
+{
+	lock_guard<Mutex> lock(mMutex);
+
+	// Stop capturing
+	mIsCapturing = false;
+
+	// Open file
+	ofstream f;
+	f.open(inFileName, ofstream::out | ofstream::trunc);
+	if (!f.is_open()) 
+		return;
+
+	// Start html file
+	f << R"(<!DOCTYPE html>
+<html>
+	<head>
+		<title>Stats</title>
+		<script type="text/javascript" src="WebIncludes/jquery-3.2.1.min.js"></script>
+		<script src="WebIncludes/dygraph.min.js"></script>
+		<link rel="stylesheet" href="WebIncludes/dygraph.min.css"/>
+		<script src="WebIncludes/jquery.fancytree-all-deps.min.js"></script>
+		<link rel="stylesheet" href="WebIncludes/ui.fancytree.min.css"/>
+		<style>
+			#labelsdiv>span { display: block; }
+			ul.fancytree-container { border: 0px; }
+		</style>	
+	</head>
+	<body>
+	<div style="width: 100%; height: 50vh;">
+		<div id="graphdiv" style="float: left; width:60%; height: 50vh; overflow: hidden;"></div>
+		<div id="labelsdiv" style="float: right; width:39%; height: 50vh; overflow-x: hidden; overflow-y: scroll;"></div>
+	</div>
+	<p>
+		<button id="btnSelectAll">Select All</button> &nbsp; 
+		<button id="btnDeselectAll">Deselect All</button> &nbsp; 
+		<input id="search" placeholder="Filter..." autocomplete="off">
+		<button id="btnResetSearch">&times;</button>
+		<span id="matches"></span>
+	</p>
+	<div style="width:100%; height: 40vh; overflow-x: hidden; overflow-y: scroll;">
+		<div id="tree" style="width:100%;">
+		</div>
+	</div>
+	<script type="text/javascript">
+		"use strict";
+)";
+
+	// Write all data points
+	f << "var point_data = [";
+	bool first = true;
+	for (const FrameMap::value_type &entry : mFrames)
+	{
+		// Don't write empty samples
+		if (entry.second.empty())
+			continue;
+
+		// Write comma at start of each next line
+		if (!first)
+			f << ",";
+		first = false;
+
+		// Write frame number
+		f << "[" << entry.first;
+
+		// Write all columns
+		for (const KeyIDMap::value_type &key : mKeys)
+		{
+			KeyValueMap::const_iterator v = entry.second.find(key.second);
+			if (v == entry.second.end())
+				f << ",NaN";
+			else
+				f << "," << v->second.ToString();
+		}
+
+		f << "]\n";
+	}
+	f << "];\n";
+
+	// Write labels
+	f << "var labels_data = [\"Frame\"";
+	for (const KeyIDMap::value_type &key : mKeys)
+		f << ",\"" << key.first << "\"";
+	f << "];\n";
+
+	// Write colors
+	f << "var colors_data = ['rgb(0,0,0)'";
+	for (int i = 0; i < (int)mKeys.size() - 1; ++i)
+	{
+		Color c = Color::sGetDistinctColor(i);
+		f << ",'rgb(" << (int)c.r << "," << (int)c.g << "," << (int)c.b << ")'";
+	}
+	f << "];\n";
+
+	// Calculate tree
+	StatTreeNode root;
+	int index = 0;
+	for (const KeyIDMap::value_type &key : mKeys)
+	{
+		// Split parts of key
+		vector<string> parts;
+		StringToVector(key.first, parts, ".");
+
+		// Create tree nodes
+		StatTreeNode *cur = &root;
+		for (string p : parts)
+			cur = &cur->mChildren[p];
+
+		// Set index on leaf node
+		cur->mIndex = index;
+		++index;
+	}
+
+	// Output tree
+	f << "var tree_data = [";
+	sWriteStatTree(f, root);
+	f << "];\n";
+
+	// Write main script
+	f << R"-(
+		var graph = new Dygraph(
+			document.getElementById("graphdiv"),
+			point_data,
+			{ 
+			labels: labels_data,
+			colors: colors_data,
+			labelsDiv: labelsdiv,
+			hideOverlayOnMouseOut: false,
+			showRangeSelector: true,
+			xlabel: "Frame",
+			ylabel: "Value"
+		});
+
+		function sync_enabled_series(tree, graph) {
+			var is_visible = [];
+			for (var i = 0; i < graph.numColumns() - 1; ++i)
+				is_visible[i] = false;
+
+			var selected_nodes = tree.getSelectedNodes(false);
+			for (var i = 0; i < selected_nodes.length; ++i)
+			{
+				var key = parseInt(selected_nodes[i].key);
+				if (key >= 0)
+					is_visible[key] = true;
+			}
+				
+			graph.setVisibility(is_visible);
+		};
+
+		$(function() {
+			$("#tree").fancytree({
+				extensions: ["filter"],
+				quicksearch: true,
+				source: tree_data,
+				icon: false,
+				checkbox: true,
+				selectMode: 3,
+				keyboard: true,
+				quicksearch: true,
+				filter: {
+					autoExpand: true,			
+					mode: "hide"
+				},
+				select: function(event, data) {
+					sync_enabled_series(tree, graph);
+				}
+			});
+		
+			var tree = $("#tree").fancytree("getTree");
+		
+			var no_events = { noEvents: true };
+
+			tree.enableUpdate(false);
+			tree.visit(function(node) {
+				node.setExpanded(true);
+				node.setSelected(true, no_events);
+			});	
+			tree.enableUpdate(true);
+		
+			$("#search").keyup(function(e) {
+				var match = $(this).val();
+				if (e && e.which === $.ui.keyCode.ESCAPE || $.trim(match) === "") {
+					$("#btnResetSearch").click();
+					return;
+				}
+				var n = tree.filterBranches.call(tree, match, { autoExpand: true });
+				$("#btnResetSearch").attr("disabled", false);
+				$("#matches").text("(" + n + " matches)");
+			}).focus();
+
+			$("#btnResetSearch").click(function(e) {
+				$("#search").val("");
+				$("#btnResetSearch").attr("disabled", true);
+				$("#matches").text("");
+				tree.clearFilter();
+			}).attr("disabled", true);
+
+			$("#btnDeselectAll").click(function() {
+				tree.enableUpdate(false);
+				tree.visit(function(node) {
+					if (node.isMatched())
+						node.setSelected(false, no_events);
+					});
+				tree.enableUpdate(true);
+				sync_enabled_series(tree, graph);
+				return false;
+			});
+		
+			$("#btnSelectAll").click(function() {
+				tree.enableUpdate(false);
+				tree.visit(function(node) {
+					if (node.isMatched())
+						node.setSelected(true, no_events);
+					});
+				tree.enableUpdate(true);
+				sync_enabled_series(tree, graph);
+				return false;
+			});
+		});
+	</script>
+	</body>
+</html>)-";
+
+	// Remove all collected data
+	ResetInternal();
+}
+
+#endif // JPH_STAT_COLLECTOR
+
+} // JPH

+ 124 - 0
Jolt/Core/StatCollector.h

@@ -0,0 +1,124 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/Mutex.h>
+#include <Core/NonCopyable.h>
+#include <map>
+
+namespace JPH {
+
+#ifdef JPH_STAT_COLLECTOR
+
+/// Singleton class for collacting simple stat values
+///
+/// Usage:
+///
+/// To start recording call:
+///
+///		JPH_STAT_COLLECTOR_START_CAPTURE()
+///
+/// Then start the next frame:
+///
+///		JPH_STAT_COLLECTOR_SET_NEXT_FRAME()
+///
+/// To add a stat:
+///
+///		STAT_COLLETOR_ADD("Path.To.Name", <value>)
+///
+/// where value is an int, float, bool, Vec3 or Quat
+///
+/// To dump the stats to a file call:
+///
+///		JPH_STAT_COLLECTOR_STOP_CAPTURE()
+class StatCollector : public NonCopyable
+{
+public:
+	/// Reset all stats
+	void						Reset();
+
+	/// Start / stop capture
+	void						StartCapture();
+	void						StopCapture(const char *inFileName);
+	bool						IsCapturing() const				{ return mIsCapturing; }
+
+	/// Increments the frame counter
+	void						SetNextFrame();
+
+	/// Helper class that stores data points
+	class Variant
+	{
+	public:
+		enum class EType
+		{
+			Undefined,
+			Float,
+			Int,
+			Bool,
+		};
+
+								Variant() : mType(EType::Undefined) { }
+								Variant(float inFloat) : mType(EType::Float), mFloat(inFloat) { }
+								Variant(int inInt) : mType(EType::Int), mInt(inInt) { }
+								Variant(bool inBool) : mType(EType::Bool), mBool(inBool) { }
+
+		string					ToString() const;
+
+	private:
+		EType					mType;
+
+		union
+		{
+			float				mFloat;
+			int					mInt;
+			bool				mBool;
+		};
+	};
+	
+	/// Add an item
+	void						AddItem(const string &inName, const Variant &inValue);
+	void						AddItem(const string &inName, Vec3Arg inValue);
+	void						AddItem(const string &inName, QuatArg inValue);
+
+	/// Singleton instance
+	static StatCollector		sInstance;
+								
+private:
+	/// Internal variant of Reset that does not take the lock
+	void						ResetInternal();
+
+	using KeyValueMap = map<int, Variant>;
+	using FrameMap = map<int, KeyValueMap>;
+	using KeyIDMap = map<string, int>;
+
+	Mutex						mMutex;
+	bool						mIsCapturing = false;
+	FrameMap					mFrames;
+	KeyIDMap					mKeys;
+	int							mNextKey = 0;
+	int							mCurrentFrameNumber = 0;
+	KeyValueMap *				mCurrentFrame = nullptr;
+};							
+
+#define JPH_IF_STAT_COLLECTOR(...)					__VA_ARGS__
+#define JPH_STAT_COLLECTOR_SET_NEXT_FRAME()			StatCollector::sInstance.SetNextFrame()
+#define JPH_STAT_COLLECTOR_ADD(name, value)			StatCollector::sInstance.AddItem(name, value)
+#define JPH_STAT_COLLECTOR_START_CAPTURE()			StatCollector::sInstance.StartCapture()
+#define JPH_STAT_COLLECTOR_STOP_CAPTURE(file_name)	StatCollector::sInstance.StopCapture(file_name)
+#define JPH_STAT_COLLECTOR_IS_CAPTURING()			StatCollector::sInstance.IsCapturing()
+#define JPH_STAT_COLLECTOR_RESET()					StatCollector::sInstance.Reset()
+
+#else
+
+#define JPH_IF_STAT_COLLECTOR(...)
+#define JPH_STAT_COLLECTOR_SET_NEXT_FRAME()
+#define JPH_STAT_COLLECTOR_ADD(name, value)
+#define JPH_STAT_COLLECTOR_START_CAPTURE()		
+#define JPH_STAT_COLLECTOR_STOP_CAPTURE(file_name)		
+#define JPH_STAT_COLLECTOR_IS_CAPTURING()			false
+#define JPH_STAT_COLLECTOR_RESET()
+
+#endif
+
+} // JPH

+ 304 - 0
Jolt/Core/StaticArray.h

@@ -0,0 +1,304 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// Simple variable length array backed by a fixed size buffer
+template <class T, uint N>
+class [[nodiscard]] StaticArray
+{
+public:
+	using value_type = T;
+
+	using size_type = uint;
+
+	/// Default constructor
+						StaticArray()
+	{
+	}
+
+	/// Constructor from initializer list
+						StaticArray(initializer_list<T> inList)
+	{
+		JPH_ASSERT(inList.size() <= N);
+		for (typename initializer_list<T>::iterator i = inList.begin(); i != inList.end(); ++i)
+			new (reinterpret_cast<T *>(&mElements[mSize++])) T(*i);
+	}
+
+	/// Copy constructor
+						StaticArray(const StaticArray<T, N> &inRHS)
+	{
+		while (mSize < inRHS.mSize)
+		{
+			new (&mElements[mSize]) T(inRHS[mSize]);
+			++mSize;
+		}
+	}
+
+	/// Destruct all elements
+						~StaticArray()
+	{
+		if (!is_trivially_destructible<T>())
+			for (T *e = reinterpret_cast<T *>(mElements), *end = e + mSize; e < end; ++e)
+				e->~T();
+	}
+
+	/// Destruct all elements and set length to zero
+	void				clear()
+	{
+		if (!is_trivially_destructible<T>())
+			for (T *e = reinterpret_cast<T *>(mElements), *end = e + mSize; e < end; ++e)
+				e->~T();
+		mSize = 0;
+	}
+
+	/// Add element to the back of the array
+	void				push_back(const T &inElement)
+	{
+		JPH_ASSERT(mSize < N);
+		new (&mElements[mSize++]) T(inElement);
+	}
+
+	/// Construct element at the back of the array
+	template <class... A>
+	void				emplace_back(A &&... inElement)
+	{	
+		JPH_ASSERT(mSize < N);
+		new (&mElements[mSize++]) T(forward<A>(inElement)...);
+	}
+
+	/// Remove element from the back of the array
+	void				pop_back()
+	{
+		JPH_ASSERT(mSize > 0);
+		reinterpret_cast<T &>(mElements[--mSize]).~T();
+	}
+
+	/// Returns true if there are no elements in the array
+	bool				empty() const
+	{
+		return mSize == 0;
+	}
+
+	/// Returns amount of elements in the array
+	size_type			size() const
+	{
+		return mSize;
+	}
+
+	/// Returns maximum amount of elements the array can hold
+	size_type			capacity() const
+	{
+		return N;
+	}
+
+	/// Resize array to new length
+	void				resize(size_type inNewSize)
+	{
+		JPH_ASSERT(inNewSize <= N);
+		if (!is_trivially_constructible<T>() && mSize < inNewSize)
+			for (T *element = reinterpret_cast<T *>(mElements) + mSize, *element_end = reinterpret_cast<T *>(mElements) + inNewSize; element < element_end; ++element)
+				new (element) T;
+		else if (!is_trivially_destructible<T>() && mSize > inNewSize)
+			for (T *element = reinterpret_cast<T *>(mElements) + inNewSize, *element_end = reinterpret_cast<T *>(mElements) + mSize; element < element_end; ++element)
+				element->~T();
+		mSize = inNewSize;
+	}
+
+	using const_iterator = const T *;
+
+	/// Iterators
+	const_iterator		begin() const
+	{
+		return reinterpret_cast<const T *>(mElements);
+	}
+
+	const_iterator		end() const
+	{
+		return reinterpret_cast<const T *>(mElements + mSize);
+	}
+
+	using iterator = T *;
+
+	iterator			begin()
+	{
+		return reinterpret_cast<T *>(mElements);
+	}
+
+	iterator			end()
+	{
+		return reinterpret_cast<T *>(mElements + mSize);
+	}
+
+	const T *			data() const
+	{
+		return reinterpret_cast<const T *>(mElements);
+	}
+
+	T *					data()
+	{
+		return reinterpret_cast<T *>(mElements);
+	}
+
+	/// Access element
+	T &					operator [] (size_type inIdx)
+	{
+		JPH_ASSERT(inIdx < mSize);
+		return reinterpret_cast<T &>(mElements[inIdx]);
+	}
+
+	const T &			operator [] (size_type inIdx) const
+	{
+		JPH_ASSERT(inIdx < mSize);
+		return reinterpret_cast<const T &>(mElements[inIdx]);
+	}
+
+	/// First element in the array
+	const T &			front() const
+	{
+		JPH_ASSERT(mSize > 0);
+		return reinterpret_cast<const T &>(mElements[0]);
+	}
+
+	T &					front()
+	{
+		JPH_ASSERT(mSize > 0);
+		return reinterpret_cast<T &>(mElements[0]);
+	}
+
+	/// Last element in the array
+	const T &			back() const
+	{
+		JPH_ASSERT(mSize > 0);
+		return reinterpret_cast<const T &>(mElements[mSize - 1]);
+	}
+
+	T &					back()
+	{
+		JPH_ASSERT(mSize > 0);
+		return reinterpret_cast<T &>(mElements[mSize - 1]);
+	}
+
+	/// Remove one element from the array
+	void				erase(const_iterator inIter)
+	{
+		size_type p = size_type(inIter - begin());
+		JPH_ASSERT(p < mSize);
+		reinterpret_cast<T &>(mElements[p]).~T();
+		if (p + 1 < mSize)
+			memmove(mElements + p, mElements + p + 1, (mSize - p - 1) * sizeof(T));
+		--mSize;
+	}
+
+	/// Remove multiple element from the array
+	void				erase(const_iterator inBegin, const_iterator inEnd)
+	{
+		size_type p = size_type(inBegin - begin());
+		size_type n = size_type(inEnd - inBegin);
+		JPH_ASSERT(inEnd <= end());
+		for (size_type i = 0; i < n; ++i)
+			reinterpret_cast<T &>(mElements[p + i]).~T();
+		if (p + n < mSize)
+			memmove(mElements + p, mElements + p + n, (mSize - p - n) * sizeof(T));
+		mSize -= n;
+	}
+
+	/// Assignment operator
+	StaticArray<T, N> &	operator = (const StaticArray<T, N> &inRHS)
+	{
+		size_type rhs_size = inRHS.size();
+
+		if ((void *)this != (void *)&inRHS)
+		{
+			clear();
+
+			while (mSize < rhs_size)
+			{
+				new (&mElements[mSize]) T(inRHS[mSize]);
+				++mSize;
+			}
+		}
+
+		return *this;
+	}
+
+	/// Assignment operator with static array of different max length
+	template <uint M>
+	StaticArray<T, N> &	operator = (const StaticArray<T, M> &inRHS)
+	{
+		size_type rhs_size = inRHS.size();
+		JPH_ASSERT(rhs_size <= N);
+
+		if ((void *)this != (void *)&inRHS)
+		{
+			clear();
+
+			while (mSize < rhs_size)
+			{
+				new (&mElements[mSize]) T(inRHS[mSize]);
+				++mSize;
+			}
+		}
+
+		return *this;
+	}
+	
+	/// Comparing arrays
+	bool				operator == (const StaticArray<T, N> &inRHS) const
+	{
+		if (mSize != inRHS.mSize)
+			return false;
+		for (size_type i = 0; i < mSize; ++i)
+			if (!(reinterpret_cast<const T &>(mElements[i]) == reinterpret_cast<const T &>(inRHS.mElements[i])))
+				return false;
+		return true;
+	}
+
+	bool				operator != (const StaticArray<T, N> &inRHS) const
+	{
+		if (mSize != inRHS.mSize)
+			return true;
+		for (size_type i = 0; i < mSize; ++i)
+			if (reinterpret_cast<const T &>(mElements[i]) != reinterpret_cast<const T &>(inRHS.mElements[i]))
+				return true;
+		return false;
+	}
+	
+protected:
+	struct alignas(T) Storage
+	{
+		uint8			mData[sizeof(T)];
+	};
+
+	static_assert(sizeof(T) == sizeof(Storage), "Mismatch in size");
+	static_assert(alignof(T) == alignof(Storage), "Mismatch in alignment");
+
+	size_type			mSize = 0;
+	Storage				mElements[N];
+};
+
+} // JPH
+
+namespace std
+{
+	/// Declare std::hash for StaticArray
+	template <class T, JPH::uint N>
+	struct hash<JPH::StaticArray<T, N>>
+	{
+		size_t operator () (const JPH::StaticArray<T, N> &inRHS) const
+		{
+			std::size_t ret = 0;
+
+			// Hash length first
+            JPH::hash_combine(ret, inRHS.size());
+
+			// Then hash elements
+			for (const T &t : inRHS)
+	            JPH::hash_combine(ret, t);
+
+            return ret;
+		}
+	};
+}

+ 62 - 0
Jolt/Core/StreamIn.h

@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// Simple binary input stream
+class StreamIn
+{
+public:
+	/// Virtual destructor
+	virtual				~StreamIn() { }
+
+	/// Read a string of bytes from the binary stream
+	virtual void		ReadBytes(void *outData, size_t inNumBytes) = 0;
+
+	/// Returns true when an attempt has been made to read past the end of the file
+	virtual bool		IsEOF() const = 0;
+
+	/// Returns true if there was an IO failure
+	virtual bool		IsFailed() const = 0;
+
+	/// Read a primitive (e.g. float, int, etc.) from the binary stream
+	template <class T>
+	void				Read(T &outT)
+	{
+		ReadBytes(&outT, sizeof(outT));
+	}
+	
+	/// Read a vector of primitives from the binary stream
+	template <class T, class A>
+	void				Read(vector<T, A> &outT)
+	{
+		typename vector<T>::size_type len = outT.size(); // Initialize to previous array size, this is used for validation in the StateRecorder class
+		Read(len);
+		if (!IsEOF() && !IsFailed())
+		{
+			outT.resize(len);
+			for (typename vector<T>::size_type i = 0; i < len; ++i)
+				Read(outT[i]);
+		}
+		else
+			outT.clear();
+	}
+
+	/// Read a string from the binary stream (reads the number of characters and then the characters)
+	void				Read(string &outString)
+	{
+		string::size_type len = 0;
+		Read(len);
+		if (!IsEOF() && !IsFailed())
+		{
+			outString.resize(len);
+			ReadBytes(outString.data(), len);
+		}
+		else
+			outString.clear();
+	}
+};
+
+} // JPH

+ 49 - 0
Jolt/Core/StreamOut.h

@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// Simple binary output stream
+class StreamOut
+{
+public:
+	/// Virtual destructor
+	virtual				~StreamOut() { }
+
+	/// Write a string of bytes to the binary stream
+	virtual void		WriteBytes(const void *inData, size_t inNumBytes) = 0;
+
+	/// Returns true if there was an IO failure
+	virtual bool		IsFailed() const = 0;
+
+	/// Write a primitive (e.g. float, int, etc.) to the binary stream
+	template <class T>
+	void				Write(const T &inT)
+	{
+		WriteBytes(&inT, sizeof(inT));
+	}
+
+	/// Write a vector of primitives from the binary stream
+	template <class T, class A>
+	void				Write(const vector<T, A> &inT)
+	{
+		typename vector<T>::size_type len = inT.size();
+		Write(len);
+		if (!IsFailed())
+			for (typename vector<T>::size_type i = 0; i < len; ++i)
+				Write(inT[i]);
+	}
+
+	/// Write a string to the binary stream (writes the number of characters and then the characters)
+	void				Write(const string &inString)
+	{
+		string::size_type len = inString.size();
+		Write(len);
+		if (!IsFailed())
+			WriteBytes(inString.data(), len);
+	}
+};
+
+} // JPH

+ 49 - 0
Jolt/Core/StreamWrapper.h

@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Core/StreamIn.h>
+#include <Core/StreamOut.h>
+#include <ostream>
+
+namespace JPH {
+
+/// Wrapper around std::ostream
+class StreamOutWrapper : public StreamOut
+{
+public:
+	/// Constructor
+						StreamOutWrapper(ostream &ioWrapped)						: mWrapped(ioWrapped) { }
+
+	/// Write a string of bytes to the binary stream
+	virtual void		WriteBytes(const void *inData, size_t inNumBytes) override	{ mWrapped.write((const char *)inData, inNumBytes); }
+
+	/// Returns true if there was an IO failure
+	virtual bool		IsFailed() const override									{ return mWrapped.fail(); }
+
+private:
+	ostream &			mWrapped;
+};
+
+/// Wrapper around std::istream
+class StreamInWrapper : public StreamIn
+{
+public:
+	/// Constructor
+						StreamInWrapper(istream &ioWrapped)							: mWrapped(ioWrapped) { }
+
+	/// Write a string of bytes to the binary stream
+	virtual void		ReadBytes(void *outData, size_t inNumBytes) override		{ mWrapped.read((char *)outData, inNumBytes); }
+
+	/// Returns true when an attempt has been made to read past the end of the file
+	virtual bool		IsEOF() const override										{ return mWrapped.eof(); }
+
+	/// Returns true if there was an IO failure
+	virtual bool		IsFailed() const override									{ return mWrapped.fail(); }
+
+private:
+	istream &			mWrapped;
+};
+
+} // JPH

+ 96 - 0
Jolt/Core/StringTools.cpp

@@ -0,0 +1,96 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/StringTools.h>
+#include <cstdarg>
+
+namespace JPH {
+
+string StringFormat(const char *inFMT, ...)
+{
+	static char buffer[1024];
+
+	// Format the string
+	va_list list;
+	va_start(list, inFMT);
+	vsnprintf(buffer, sizeof(buffer), inFMT, list);
+
+	return string(buffer);
+}
+
+void StringReplace(string &ioString, string inSearch, string inReplace)
+{
+	size_t index = 0;
+	for (;;)
+	{
+		 index = ioString.find(inSearch, index);
+		 if (index == std::string::npos) 
+			 break;
+
+		 ioString.replace(index, inSearch.size(), inReplace);
+
+		 index += inReplace.size();
+	}
+}
+
+void StringToVector(const string &inString, vector<string> &outVector, const string &inDelimiter, bool inClearVector)
+{
+	JPH_ASSERT(inDelimiter.size() > 0);
+
+	// Ensure vector empty
+	if (inClearVector)
+		outVector.clear(); 
+
+	// No string? no elements
+	if (inString.empty())
+		return;
+
+	// Start with initial string
+	string s(inString);
+
+	// Add to vector while we have a delimiter
+	size_t i;
+	while (!s.empty() && (i = s.find(inDelimiter)) != string::npos)
+	{
+		outVector.push_back(s.substr(0, i));
+		s.erase(0, i + inDelimiter.length());
+	}
+
+	// Add final element
+	outVector.push_back(s);
+}
+
+void VectorToString(const vector<string> &inVector, string &outString, const string &inDelimiter)
+{
+	// Ensure string empty
+	outString.clear();
+
+	for (vector<string>::const_iterator i = inVector.begin(); i != inVector.end(); ++i)
+	{
+		// Add delimiter if not first element
+		if (!outString.empty())
+			outString.append(inDelimiter);
+
+		// Add element
+		outString.append(*i);
+	}
+}
+
+string ToLower(const string &inString)
+{
+	string out;
+	out.reserve(inString.length());
+	for (char c : inString)
+		out.push_back((char)tolower(c));
+	return out;
+}
+
+const char *NibbleToBinary(uint32 inNibble)
+{
+	static const char *nibbles[] = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
+	return nibbles[inNibble & 0xf];
+}
+
+} // JPH

+ 48 - 0
Jolt/Core/StringTools.h

@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// Create a formatted text string
+string StringFormat(const char *inFMT, ...);
+
+/// Convert type to string
+template<typename T>
+string ConvertToString(const T &inValue)
+{
+    ostringstream oss;
+    oss << inValue;
+    return oss.str();
+}
+
+/// Calculate the FNV-1a hash of inString.
+/// @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+constexpr uint64 HashString(const char *inString)
+{
+	uint64 hash = 14695981039346656037UL;
+	for (const char *c = inString; *c != 0; ++c)
+	{
+		hash ^= *c;
+		hash = hash * 1099511628211UL;
+	}
+	return hash;
+}
+
+/// Replace substring with other string
+void StringReplace(string &ioString, string inSearch, string inReplace);
+
+/// Convert a delimited string to an array of strings
+void StringToVector(const string &inString, vector<string> &outVector, const string &inDelimiter = ",", bool inClearVector = true);
+
+/// Convert an array strings to a delimited string
+void VectorToString(const vector<string> &inVector, string &outString, const string &inDelimiter = ",");
+
+/// Convert a string to lower case
+string ToLower(const string &inString);
+
+/// Converts the lower 4 bits of inNibble to a string that represents the number in binary format
+const char *NibbleToBinary(uint32 inNibble);
+
+} // JPH

+ 100 - 0
Jolt/Core/TempAllocator.h

@@ -0,0 +1,100 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+/// Allocator for temporary allocations. 
+/// This allocator works as a stack: The blocks must always be freed in the reverse order as they are allocated.
+/// Note that allocations and frees can take place from different threads, but the order is guaranteed though
+/// job dependencies, so it is not needed to use any form of locking.
+class TempAllocator
+{
+public:
+	/// Destructor
+	virtual							~TempAllocator() { }
+
+	/// Allocates inSize bytes of memory, returned memory address must be 16 byte aligned
+	virtual void *					Allocate(uint inSize) = 0;
+
+	/// Frees inSize bytes of memory located at inAddress
+	virtual void					Free(void *inAddress, uint inSize) = 0;
+};
+
+/// Default implementation of the temp allocator that allocates a large block through malloc upfront
+class TempAllocatorImpl final : public TempAllocator
+{
+public:
+	/// Constructs the allocator with a maximum allocatable size of inSize
+									TempAllocatorImpl(uint inSize) :
+		mBase(static_cast<uint8 *>(malloc(inSize))),
+		mSize(inSize)
+	{
+	}
+
+	/// Destructor, frees the block
+	virtual							~TempAllocatorImpl() override
+	{
+		JPH_ASSERT(mTop == 0);
+		free(mBase);
+	}
+
+	// See: TempAllocator
+	virtual void *					Allocate(uint inSize) override
+	{
+		if (inSize == 0)
+		{
+			return nullptr;
+		}
+		else
+		{
+			uint new_top = mTop + AlignUp(inSize, 16);
+			if (new_top > mSize)
+				JPH_CRASH; // Out of memory
+			void *address = mBase + mTop;
+			mTop = new_top;
+			return address;
+		}
+	}
+
+	// See: TempAllocator
+	virtual void					Free(void *inAddress, uint inSize) override
+	{
+		if (inAddress == nullptr)
+		{
+			JPH_ASSERT(inSize == 0);
+		}
+		else
+		{
+			mTop -= AlignUp(inSize, 16);
+			if (mBase + mTop != inAddress)
+				JPH_CRASH; // Freeing in the wrong order
+		}
+	}
+
+private:
+	uint8 *							mBase;							///< Base address of the memory block
+	uint							mSize;							///< Size of the memory block
+	uint							mTop = 0;						///< Current top of the stack
+};
+
+/// Implementation of the TempAllocator that just falls back to malloc/free
+/// Note: This can be quite slow when running in the debugger as large memory blocks need to be initialized with 0xcd
+class TempAllocatorMalloc final : public TempAllocator
+{
+public:
+	// See: TempAllocator
+	virtual void *					Allocate(uint inSize) override
+	{
+		return malloc(inSize);
+	}
+
+	// See: TempAllocator
+	virtual void					Free(void *inAddress, uint inSize) override
+	{
+		free(inAddress);
+	}
+};
+
+}; // JPH

+ 86 - 0
Jolt/Core/TickCounter.cpp

@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Core/TickCounter.h>
+
+#if defined(JPH_PLATFORM_WINDOWS)
+	#pragma warning (push, 0)
+	#pragma warning (disable : 5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception.
+	#define WIN32_LEAN_AND_MEAN
+	#include <windows.h>
+	#pragma warning (pop)
+#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID)
+	#include <fstream>
+#endif
+
+namespace JPH {
+
+static uint64 sProcessorTicksPerSecond = []() {
+#if defined(JPH_PLATFORM_WINDOWS)
+	// Open the key where the processor speed is stored
+	HKEY hkey;
+	RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, 1, &hkey);
+
+	// Query the speed in MHz
+	uint mhz = 0;
+	DWORD mhz_size = sizeof(uint);
+	RegQueryValueExA(hkey, "~MHz", nullptr, nullptr, (LPBYTE)&mhz, &mhz_size);
+
+	// Close key
+	RegCloseKey(hkey);
+
+	// Initialize amount of cycles per second
+	return uint64(mhz) * 1000000UL;
+#elif defined(JPH_PLATFORM_BLUE)
+	return JPH_PLATFORM_BLUE_GET_TICK_FREQUENCY();
+#elif defined(JPH_PLATFORM_LINUX) || defined(JPH_PLATFORM_ANDROID)
+	// Open /proc/cpuinfo
+    ifstream ifs("/proc/cpuinfo");
+    if (ifs.is_open())
+	{
+		// Read all lines
+		while (ifs.good())
+		{
+			// Get next line
+			string line;
+			getline(ifs, line);
+		
+		#if defined(JPH_CPU_X64)
+			const char *cpu_str = "cpu MHz";
+		#elif defined(JPH_CPU_ARM64)
+			const char *cpu_str = "BogoMIPS";
+		#else
+			#error Unsupported CPU architecture
+		#endif
+
+			// Check if line starts with correct string
+			const int num_chars = strlen(cpu_str);
+			if (strncmp(line.c_str(), cpu_str, num_chars) == 0)
+			{
+				// Find ':'
+				string::size_type pos = line.find(':', num_chars);
+				if (pos != string::npos)
+				{		
+					// Convert to number
+					string freq = line.substr(pos + 1);
+					return uint64(stod(freq) * 1000000.0);
+				}
+			}
+		}
+	}
+
+	JPH_ASSERT(false);
+    return uint64(0);
+#else
+	#error Undefined
+#endif
+}();
+
+uint64 GetProcessorTicksPerSecond()
+{
+	return sProcessorTicksPerSecond;
+}
+
+} // JPH

+ 31 - 0
Jolt/Core/TickCounter.h

@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#ifdef JPH_PLATFORM_WINDOWS
+	#include <intrin.h> // for __rdtsc
+#endif
+
+namespace JPH {
+
+/// Functionality to get the processors cycle counter 
+JPH_INLINE uint64 GetProcessorTickCount()
+{
+#if defined(JPH_PLATFORM_BLUE)
+	return JPH_PLATFORM_BLUE_GET_TICKS();
+#elif defined(JPH_CPU_X64)
+	return __rdtsc();
+#elif defined(JPH_CPU_ARM64)
+	uint64 val;
+    asm volatile("mrs %0, cntvct_el0" : "=r" (val));
+	return val;
+#else
+	#error Undefined
+#endif
+}
+
+/// Get the amount of ticks per second, note that this number will never be fully accurate as the amound of ticks per second may vary with CPU load, so this number is only to be used to give an indication of time for profiling purposes
+uint64 GetProcessorTicksPerSecond();
+
+} // JPH

+ 279 - 0
Jolt/Geometry/AABox.h

@@ -0,0 +1,279 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Geometry/Triangle.h>
+#include <Geometry/IndexedTriangle.h>
+#include <Geometry/Plane.h>
+#include <Math/Mat44.h>
+
+namespace JPH {
+
+/// Axis aligned box
+class [[nodiscard]] AABox
+{
+public:
+	/// Constructor
+					AABox()												: mMin(Vec3::sReplicate(FLT_MAX)), mMax(Vec3::sReplicate(-FLT_MAX)) { }
+					AABox(Vec3Arg inMin, Vec3Arg inMax)					: mMin(inMin), mMax(inMax) { }
+					AABox(Vec3Arg inCenter, float inRadius)				: mMin(inCenter - Vec3::sReplicate(inRadius)), mMax(inCenter + Vec3::sReplicate(inRadius)) { }
+
+	/// Create box from 2 points
+	static AABox	sFromTwoPoints(Vec3Arg inP1, Vec3Arg inP2)			{ return AABox(Vec3::sMin(inP1, inP2), Vec3::sMax(inP1, inP2)); }
+
+	/// Get bounding box of size 2 * FLT_MAX
+	static AABox	sBiggest()
+	{
+		return AABox(Vec3::sReplicate(-FLT_MAX), Vec3::sReplicate(FLT_MAX));
+	}
+
+	/// Comparison operators
+	bool			operator == (const AABox &inRHS) const				{ return mMin == inRHS.mMin && mMax == inRHS.mMax; }
+	bool			operator != (const AABox &inRHS) const				{ return mMin != inRHS.mMin || mMax != inRHS.mMax; }
+
+	/// Reset the bounding box to an empty bounding box
+	void			SetEmpty()
+	{
+		mMin = Vec3::sReplicate(FLT_MAX);
+		mMax = Vec3::sReplicate(-FLT_MAX);
+	}
+
+	/// Check if the bounding box is valid (max >= min)
+	bool			IsValid() const
+	{
+		return mMin.GetX() <= mMax.GetX() && mMin.GetY() <= mMax.GetY() && mMin.GetZ() <= mMax.GetZ();
+	}
+
+	/// Encapsulate point in bounding box
+	void			Encapsulate(Vec3Arg inPos)						
+	{ 
+		mMin = Vec3::sMin(mMin, inPos); 
+		mMax = Vec3::sMax(mMax, inPos); 
+	}
+
+	/// Encapsulate bounding box in bounding box
+	void			Encapsulate(const AABox &inRHS)			
+	{ 
+		mMin = Vec3::sMin(mMin, inRHS.mMin);
+		mMax = Vec3::sMax(mMax, inRHS.mMax);
+	}
+
+	/// Encapsulate triangle in bounding box
+	void			Encapsulate(const Triangle &inRHS)
+	{
+		Vec3 v = Vec3::sLoadFloat3Unsafe(inRHS.mV[0]);
+		Encapsulate(v);
+		v = Vec3::sLoadFloat3Unsafe(inRHS.mV[1]);
+		Encapsulate(v);
+		v = Vec3::sLoadFloat3Unsafe(inRHS.mV[2]);
+		Encapsulate(v);
+	}
+
+	/// Encapsulate triangle in bounding box
+	void			Encapsulate(const VertexList &inVertices, const IndexedTriangle &inTriangle)
+	{
+		for (uint32 idx : inTriangle.mIdx)
+			Encapsulate(Vec3(inVertices[idx]));
+	}
+
+	/// Intersect this bounding box with inOther, returns the intersection
+	const AABox		Intersect(const AABox &inOther) const
+	{
+		return AABox(Vec3::sMax(mMin, inOther.mMin), Vec3::sMin(mMax, inOther.mMax));
+	}
+
+	/// Make sure that each edge of the bounding box has a minimal length
+	void			EnsureMinimalEdgeLength(float inMinEdgeLength)
+	{
+		Vec3 min_length = Vec3::sReplicate(inMinEdgeLength);
+		mMax = Vec3::sSelect(mMax, mMin + min_length, Vec3::sLess(mMax - mMin, min_length));
+	}
+	
+	/// Widen the box on both sides by inVector
+	void			ExpandBy(Vec3Arg inVector)
+	{
+		mMin -= inVector;
+		mMax += inVector;
+	}
+
+	/// Get center of bounding box
+	const Vec3		GetCenter() const
+	{
+		return 0.5f * (mMin + mMax);
+	}
+
+	/// Get extent of bounding box (half of the size)
+	const Vec3		GetExtent() const
+	{
+		return 0.5f * (mMax - mMin);
+	}
+
+	/// Get size of bounding box
+	const Vec3		GetSize() const
+	{
+		return mMax - mMin;
+	}
+
+	/// Get surface area of bounding box
+	float			GetSurfaceArea() const							
+	{ 
+		Vec3 extent = mMax - mMin;
+		return 2.0f * (extent.GetX() * extent.GetY() + extent.GetX() * extent.GetZ() + extent.GetY() * extent.GetZ());
+	}
+
+	/// Get volume of bounding box
+	float			GetVolume() const
+	{
+		Vec3 extent = mMax - mMin;
+		return extent.GetX() * extent.GetY() * extent.GetZ();
+	}
+
+	/// Check if this box contains another box
+	bool			Contains(const AABox &inOther) const
+	{
+		return UVec4::sAnd(Vec3::sLessOrEqual(mMin, inOther.mMin), Vec3::sGreaterOrEqual(mMax, inOther.mMax)).TestAllXYZTrue();
+	}
+
+	/// Check if this box contains a point
+	bool			Contains(Vec3Arg inOther) const
+	{
+		return UVec4::sAnd(Vec3::sLessOrEqual(mMin, inOther), Vec3::sGreaterOrEqual(mMax, inOther)).TestAllXYZTrue();
+	}
+
+	/// Check if this box overlaps with another box
+	bool			Overlaps(const AABox &inOther) const
+	{
+		return !UVec4::sOr(Vec3::sGreater(mMin, inOther.mMax), Vec3::sLess(mMax, inOther.mMin)).TestAnyXYZTrue();
+	}
+
+	/// Check if this box overlaps with a plane
+	bool			Overlaps(const Plane &inPlane) const
+	{
+		Vec3 normal = inPlane.GetNormal();
+		float dist_normal = inPlane.SignedDistance(GetSupport(normal));
+		float dist_min_normal = inPlane.SignedDistance(GetSupport(-normal));
+		return dist_normal * dist_min_normal <= 0.0f; // If both support points are on the same side of the plane we don't overlap
+	}
+
+	/// Translate bounding box
+	void			Translate(Vec3Arg inTranslation)
+	{
+		mMin += inTranslation;
+		mMax += inTranslation;
+	}
+
+	/// Transform bounding box
+	AABox			Transformed(Mat44Arg inMatrix) const
+	{
+		// Start with the translation of the matrix
+		Vec3 new_min, new_max;
+		new_min = new_max = inMatrix.GetTranslation();
+		
+		// Now find the extreme points by considering the product of the min and max with each column of inMatrix
+		for (int c = 0; c < 3; ++c)
+		{
+			Vec3 col = inMatrix.GetColumn3(c);
+
+			Vec3 a = col * mMin[c];
+			Vec3 b = col * mMax[c];
+
+			new_min += Vec3::sMin(a, b);
+			new_max += Vec3::sMax(a, b);
+		}
+
+		// Return the new bounding box
+		return AABox(new_min, new_max);
+	}
+
+	/// Scale this bounding box, can handle non-uniform and negative scaling
+	AABox			Scaled(Vec3Arg inScale) const
+	{
+		return AABox::sFromTwoPoints(mMin * inScale, mMax * inScale);
+	}
+
+	/// Calculate the support vector for this convex shape.
+	const Vec3		GetSupport(Vec3Arg inDirection) const
+	{
+		return Vec3::sSelect(mMax, mMin, Vec3::sLess(inDirection, Vec3::sZero()));
+	}
+
+	/// Get the vertices of the face that faces inDirection the most
+	template <class VERTEX_ARRAY>
+	void			GetSupportingFace(Vec3Arg inDirection, VERTEX_ARRAY &outVertices) const
+	{
+		outVertices.resize(4);
+
+		int axis = inDirection.Abs().GetHighestComponentIndex();
+		if (inDirection[axis] < 0.0f)
+		{
+			switch (axis)
+			{
+			case 0:
+				outVertices[0] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ());
+				outVertices[1] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ());
+				outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ());
+				outVertices[3] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ());
+				break;
+
+			case 1:
+				outVertices[0] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ());
+				outVertices[1] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ());
+				outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ());
+				outVertices[3] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ());
+				break;
+
+			case 2:
+				outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ());
+				outVertices[1] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ());
+				outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMax.GetZ());
+				outVertices[3] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ());
+				break;
+			}
+		}
+		else
+		{
+			switch (axis)
+			{
+			case 0:
+				outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ());
+				outVertices[1] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ());
+				outVertices[2] = Vec3(mMin.GetX(), mMax.GetY(), mMax.GetZ());
+				outVertices[3] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ());
+				break;
+
+			case 1:
+				outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ());
+				outVertices[1] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ());
+				outVertices[2] = Vec3(mMax.GetX(), mMin.GetY(), mMax.GetZ());
+				outVertices[3] = Vec3(mMin.GetX(), mMin.GetY(), mMax.GetZ());
+				break;
+
+			case 2:
+				outVertices[0] = Vec3(mMin.GetX(), mMin.GetY(), mMin.GetZ());
+				outVertices[1] = Vec3(mMin.GetX(), mMax.GetY(), mMin.GetZ());
+				outVertices[2] = Vec3(mMax.GetX(), mMax.GetY(), mMin.GetZ());
+				outVertices[3] = Vec3(mMax.GetX(), mMin.GetY(), mMin.GetZ());
+				break;
+			}
+		}
+	}
+
+	/// Get the closest point on or in this box to inPoint
+	inline Vec3		GetClosestPoint(Vec3Arg inPoint) const
+	{
+		return Vec3::sMin(Vec3::sMax(inPoint, mMin), mMax);
+	}
+
+	/// Get the squared distance between inPoint and this box (will be 0 if in Point is inside the box)
+	inline float	GetSqDistanceTo(Vec3Arg inPoint) const
+	{
+		return (GetClosestPoint(inPoint) - inPoint).LengthSq();
+	}
+
+	/// Bounding box min and max
+	Vec3			mMin;
+	Vec3			mMax;
+};
+
+} // JPH

+ 191 - 0
Jolt/Geometry/AABox4.h

@@ -0,0 +1,191 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Geometry/OrientedBox.h>
+
+namespace JPH {
+
+/// Helper functions that process 4 axis aligned boxes at the same time using SIMD
+/// Test if 4 bounding boxes overlap with 1 bounding box, splat 1 box
+JPH_INLINE UVec4 AABox4VsBox(const AABox &inBox1, Vec4Arg inBox2MinX, Vec4Arg inBox2MinY, Vec4Arg inBox2MinZ, Vec4Arg inBox2MaxX, Vec4Arg inBox2MaxY, Vec4Arg inBox2MaxZ)
+{
+	// Splat values of box 1
+	Vec4 box1_minx = inBox1.mMin.SplatX();
+	Vec4 box1_miny = inBox1.mMin.SplatY();
+	Vec4 box1_minz = inBox1.mMin.SplatZ();
+	Vec4 box1_maxx = inBox1.mMax.SplatX();
+	Vec4 box1_maxy = inBox1.mMax.SplatY();
+	Vec4 box1_maxz = inBox1.mMax.SplatZ();
+
+	// Test separation over each axis
+	UVec4 nooverlapx = UVec4::sOr(Vec4::sGreater(box1_minx, inBox2MaxX), Vec4::sGreater(inBox2MinX, box1_maxx));
+	UVec4 nooverlapy = UVec4::sOr(Vec4::sGreater(box1_miny, inBox2MaxY), Vec4::sGreater(inBox2MinY, box1_maxy));
+	UVec4 nooverlapz = UVec4::sOr(Vec4::sGreater(box1_minz, inBox2MaxZ), Vec4::sGreater(inBox2MinZ, box1_maxz));
+
+	// Return overlap
+	return UVec4::sNot(UVec4::sOr(UVec4::sOr(nooverlapx, nooverlapy), nooverlapz));
+}
+
+/// Scale 4 bounding boxes
+JPH_INLINE void AABox4Scale(Vec3Arg inScale, Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, Vec4 &outBoundsMinX, Vec4 &outBoundsMinY, Vec4 &outBoundsMinZ, Vec4 &outBoundsMaxX, Vec4 &outBoundsMaxY, Vec4 &outBoundsMaxZ)
+{
+	Vec4 scale_x = inScale.SplatX();
+	Vec4 scaled_min_x = scale_x * inBoundsMinX;
+	Vec4 scaled_max_x = scale_x * inBoundsMaxX;
+	outBoundsMinX = Vec4::sMin(scaled_min_x, scaled_max_x); // Negative scale can flip min and max
+	outBoundsMaxX = Vec4::sMax(scaled_min_x, scaled_max_x);
+
+	Vec4 scale_y = inScale.SplatY();
+	Vec4 scaled_min_y = scale_y * inBoundsMinY;
+	Vec4 scaled_max_y = scale_y * inBoundsMaxY;
+	outBoundsMinY = Vec4::sMin(scaled_min_y, scaled_max_y);
+	outBoundsMaxY = Vec4::sMax(scaled_min_y, scaled_max_y);
+
+	Vec4 scale_z = inScale.SplatZ();
+	Vec4 scaled_min_z = scale_z * inBoundsMinZ;
+	Vec4 scaled_max_z = scale_z * inBoundsMaxZ;
+	outBoundsMinZ = Vec4::sMin(scaled_min_z, scaled_max_z);
+	outBoundsMaxZ = Vec4::sMax(scaled_min_z, scaled_max_z);
+}
+
+/// Enlarge 4 bounding boxes with extent (add to both sides)
+JPH_INLINE void AABox4EnlargeWithExtent(Vec3Arg inExtent, Vec4 &ioBoundsMinX, Vec4 &ioBoundsMinY, Vec4 &ioBoundsMinZ, Vec4 &ioBoundsMaxX, Vec4 &ioBoundsMaxY, Vec4 &ioBoundsMaxZ)
+{
+	Vec4 extent_x = inExtent.SplatX();
+	ioBoundsMinX -= extent_x;
+	ioBoundsMaxX += extent_x;
+
+	Vec4 extent_y = inExtent.SplatY();
+	ioBoundsMinY -= extent_y;
+	ioBoundsMaxY += extent_y;
+
+	Vec4 extent_z = inExtent.SplatZ();
+	ioBoundsMinZ -= extent_z;
+	ioBoundsMaxZ += extent_z;
+}
+
+/// Test if 4 bounding boxes overlap with a point
+JPH_INLINE UVec4 AABox4VsPoint(Vec3Arg inPoint, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ)
+{
+	// Splat point to 4 component vectors
+	Vec4 point_x = Vec4(inPoint).SplatX();
+	Vec4 point_y = Vec4(inPoint).SplatY();
+	Vec4 point_z = Vec4(inPoint).SplatZ();
+
+	// Test if point overlaps with box
+	UVec4 overlapx = UVec4::sAnd(Vec4::sGreaterOrEqual(point_x, inBoxMinX), Vec4::sLessOrEqual(point_x, inBoxMaxX));
+	UVec4 overlapy = UVec4::sAnd(Vec4::sGreaterOrEqual(point_y, inBoxMinY), Vec4::sLessOrEqual(point_y, inBoxMaxY));
+	UVec4 overlapz = UVec4::sAnd(Vec4::sGreaterOrEqual(point_z, inBoxMinZ), Vec4::sLessOrEqual(point_z, inBoxMaxZ));
+
+	// Test if all are overlapping
+	return UVec4::sAnd(UVec4::sAnd(overlapx, overlapy), overlapz);
+}
+
+/// Test if 4 bounding boxes overlap with an oriented box
+JPH_INLINE UVec4 AABox4VsBox(Mat44Arg inOrientation, Vec3Arg inHalfExtents, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ, float inEpsilon = 1.0e-6f)
+{
+	// Taken from: Real Time Collision Detection - Christer Ericson
+	// Chapter 4.4.1, page 103-105.
+	// Note that the code is swapped around: A is the aabox and B is the oriented box (this saves us from having to invert the orientation of the oriented box)
+
+	// Compute translation vector t (the translation of B in the space of A)
+	Vec4 t[3] { 
+		inOrientation.GetTranslation().SplatX() - 0.5f * (inBoxMinX + inBoxMaxX), 
+		inOrientation.GetTranslation().SplatY() - 0.5f * (inBoxMinY + inBoxMaxY), 
+		inOrientation.GetTranslation().SplatZ() - 0.5f * (inBoxMinZ + inBoxMaxZ) };
+	
+	// Compute common subexpressions. Add in an epsilon term to
+	// counteract arithmetic errors when two edges are parallel and
+	// their cross product is (near) null (see text for details)
+	Vec3 epsilon = Vec3::sReplicate(inEpsilon);
+	Vec3 abs_r[3] { inOrientation.GetAxisX().Abs() + epsilon, inOrientation.GetAxisY().Abs() + epsilon, inOrientation.GetAxisZ().Abs() + epsilon };
+
+	// Half extents for a
+	Vec4 a_half_extents[3] { 
+		0.5f * (inBoxMaxX - inBoxMinX), 
+		0.5f * (inBoxMaxY - inBoxMinY), 
+		0.5f * (inBoxMaxZ - inBoxMinZ) };
+
+	// Half extents of b
+	Vec4 b_half_extents_x = inHalfExtents.SplatX();
+	Vec4 b_half_extents_y = inHalfExtents.SplatY();
+	Vec4 b_half_extents_z = inHalfExtents.SplatZ();
+
+	// Each component corresponds to 1 overlapping OBB vs ABB
+	UVec4 overlaps = UVec4(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff);
+
+	// Test axes L = A0, L = A1, L = A2
+	Vec4 ra, rb;
+	for (int i = 0; i < 3; i++) 
+	{
+		ra = a_half_extents[i];
+		rb = b_half_extents_x * abs_r[0][i] + b_half_extents_y * abs_r[1][i] + b_half_extents_z * abs_r[2][i];
+		overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual(t[i].Abs(), ra + rb));
+	}
+
+	// Test axes L = B0, L = B1, L = B2
+	for (int i = 0; i < 3; i++) 
+	{
+		ra = a_half_extents[0] * abs_r[i][0] + a_half_extents[1] * abs_r[i][1] + a_half_extents[2] * abs_r[i][2];
+		rb = Vec4::sReplicate(inHalfExtents[i]);
+		overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(0, i) + t[1] * inOrientation(1, i) + t[2] * inOrientation(2, i)).Abs(), ra + rb));
+	}
+
+	// Test axis L = A0 x B0
+	ra = a_half_extents[1] * abs_r[0][2] + a_half_extents[2] * abs_r[0][1];
+	rb = b_half_extents_y * abs_r[2][0] + b_half_extents_z * abs_r[1][0];
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 0) - t[1] * inOrientation(2, 0)).Abs(), ra + rb));
+
+	// Test axis L = A0 x B1
+	ra = a_half_extents[1] * abs_r[1][2] + a_half_extents[2] * abs_r[1][1];
+	rb = b_half_extents_x * abs_r[2][0] + b_half_extents_z * abs_r[0][0];
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 1) - t[1] * inOrientation(2, 1)).Abs(), ra + rb));
+
+	// Test axis L = A0 x B2
+	ra = a_half_extents[1] * abs_r[2][2] + a_half_extents[2] * abs_r[2][1];
+	rb = b_half_extents_x * abs_r[1][0] + b_half_extents_y * abs_r[0][0];
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[2] * inOrientation(1, 2) - t[1] * inOrientation(2, 2)).Abs(), ra + rb));
+
+	// Test axis L = A1 x B0
+	ra = a_half_extents[0] * abs_r[0][2] + a_half_extents[2] * abs_r[0][0];
+	rb = b_half_extents_y * abs_r[2][1] + b_half_extents_z * abs_r[1][1];	
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 0) - t[2] * inOrientation(0, 0)).Abs(), ra + rb));
+
+	// Test axis L = A1 x B1
+	ra = a_half_extents[0] * abs_r[1][2] + a_half_extents[2] * abs_r[1][0];
+	rb = b_half_extents_x * abs_r[2][1] + b_half_extents_z * abs_r[0][1];
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 1) - t[2] * inOrientation(0, 1)).Abs(), ra + rb));
+	
+	// Test axis L = A1 x B2
+	ra = a_half_extents[0] * abs_r[2][2] + a_half_extents[2] * abs_r[2][0];
+	rb = b_half_extents_x * abs_r[1][1] + b_half_extents_y * abs_r[0][1];
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[0] * inOrientation(2, 2) - t[2] * inOrientation(0, 2)).Abs(), ra + rb));
+	
+	// Test axis L = A2 x B0
+	ra = a_half_extents[0] * abs_r[0][1] + a_half_extents[1] * abs_r[0][0];
+	rb = b_half_extents_y * abs_r[2][2] + b_half_extents_z * abs_r[1][2];
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 0) - t[0] * inOrientation(1, 0)).Abs(), ra + rb));
+	
+	// Test axis L = A2 x B1
+	ra = a_half_extents[0] * abs_r[1][1] + a_half_extents[1] * abs_r[1][0];
+	rb = b_half_extents_x * abs_r[2][2] + b_half_extents_z * abs_r[0][2];
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 1) - t[0] * inOrientation(1, 1)).Abs(), ra + rb));
+	
+	// Test axis L = A2 x B2
+	ra = a_half_extents[0] * abs_r[2][1] + a_half_extents[1] * abs_r[2][0];
+	rb = b_half_extents_x * abs_r[1][2] + b_half_extents_y * abs_r[0][2];
+	overlaps = UVec4::sAnd(overlaps, Vec4::sLessOrEqual((t[1] * inOrientation(0, 2) - t[0] * inOrientation(1, 2)).Abs(), ra + rb));
+	
+	// Return if the OBB vs AABBs are intersecting
+	return overlaps;
+}
+
+/// Convenience function that tests 4 AABoxes vs OrientedBox
+JPH_INLINE UVec4 AABox4VsBox(const OrientedBox &inBox, Vec4Arg inBoxMinX, Vec4Arg inBoxMinY, Vec4Arg inBoxMinZ, Vec4Arg inBoxMaxX, Vec4Arg inBoxMaxY, Vec4Arg inBoxMaxZ, float inEpsilon = 1.0e-6f)
+{
+	return AABox4VsBox(inBox.mOrientation, inBox.mHalfExtents, inBoxMinX, inBoxMinY, inBoxMinZ, inBoxMaxX, inBoxMaxY, inBoxMaxZ, inEpsilon);
+}
+
+} // JPH

+ 196 - 0
Jolt/Geometry/ClipPoly.h

@@ -0,0 +1,196 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <Geometry/AABox.h>
+
+namespace JPH {
+
+/// Clip inPolygonToClip against the positive halfspace of plane defined by inPlaneOrigin and inPlaneNormal.
+/// inPlaneNormal does not need to be normalized.
+template <class VERTEX_ARRAY>
+void ClipPolyVsPlane(const VERTEX_ARRAY &inPolygonToClip, Vec3Arg inPlaneOrigin, Vec3Arg inPlaneNormal, VERTEX_ARRAY &outClippedPolygon)
+{
+	JPH_ASSERT(inPolygonToClip.size() >= 2);
+	JPH_ASSERT(outClippedPolygon.empty());
+
+	// Determine state of last point
+	Vec3 e1 = inPolygonToClip[inPolygonToClip.size() - 1];
+	float prev_num = (inPlaneOrigin - e1).Dot(inPlaneNormal);
+	bool prev_inside = prev_num < 0.0f; 
+
+	// Loop through all vertices
+	for (typename VERTEX_ARRAY::size_type j = 0; j < inPolygonToClip.size(); ++j)
+	{
+		// Check if second point is inside
+		Vec3Arg e2 = inPolygonToClip[j];
+		float num = (inPlaneOrigin - e2).Dot(inPlaneNormal);
+		bool cur_inside = num < 0.0f;
+
+		// In -> Out or Out -> In: Add point on clipping plane
+		if (cur_inside != prev_inside)
+		{
+			// Solve: (X - inPlaneOrigin) . inPlaneNormal = 0 and X = e1 + t * (e2 - e1) for X
+			Vec3 e12 = e2 - e1;
+			float denom = e12.Dot(inPlaneNormal);
+			outClippedPolygon.push_back(e1 + (prev_num / denom) * e12);
+		}
+
+		// Point inside, add it
+		if (cur_inside)
+			outClippedPolygon.push_back(e2);
+
+		// Update previous state
+		prev_num = num;
+		prev_inside = cur_inside;
+		e1 = e2;
+	}
+}
+
+/// Clip polygon versus polygon.
+/// Both polygons are assumed to be in counter clockwise order.
+/// @param inClippingPolygonNormal is used to create planes of all edges in inClippingPolygon against which inPolygonToClip is clipped, inClippingPolygonNormal does not need to be normalized
+/// @param inClippingPolygon is the polygon which inClippedPolygon is clipped against
+/// @param inPolygonToClip is the polygon that is clipped
+/// @param outClippedPolygon will contain clipped polygon when function returns
+template <class VERTEX_ARRAY>
+void ClipPolyVsPoly(const VERTEX_ARRAY &inPolygonToClip, const VERTEX_ARRAY &inClippingPolygon, Vec3Arg inClippingPolygonNormal, VERTEX_ARRAY &outClippedPolygon)
+{
+	JPH_ASSERT(inPolygonToClip.size() >= 2);
+	JPH_ASSERT(inClippingPolygon.size() >= 3);
+
+	VERTEX_ARRAY tmp_vertices[2];
+	int tmp_vertices_idx = 0;
+
+	for (typename VERTEX_ARRAY::size_type i = 0; i < inClippingPolygon.size(); ++i)
+	{
+		// Get edge to clip against
+		Vec3 clip_e1 = inClippingPolygon[i];
+		Vec3 clip_e2 = inClippingPolygon[(i + 1) % inClippingPolygon.size()];
+		Vec3 clip_normal = inClippingPolygonNormal.Cross(clip_e2 - clip_e1); // Pointing inward to the clipping polygon
+		
+		// Get source and target polygon
+		const VERTEX_ARRAY &src_polygon = (i == 0)? inPolygonToClip : tmp_vertices[tmp_vertices_idx];
+		tmp_vertices_idx ^= 1;
+		VERTEX_ARRAY &tgt_polygon = (i == inClippingPolygon.size() - 1)? outClippedPolygon : tmp_vertices[tmp_vertices_idx];
+		tgt_polygon.clear();
+		
+		// Clip against the edge
+		ClipPolyVsPlane(src_polygon, clip_e1, clip_normal, tgt_polygon);
+
+		// Break out if no polygon left
+		if (tgt_polygon.size() < 3)
+		{
+			outClippedPolygon.clear();
+			break;
+		}
+	}
+}
+
+/// Clip inPolygonToClip against an edge, the edge is projected on inPolygonToClip using inClippingEdgeNormal.
+/// The positive half space (the side on the edge in the direction of inClippingEdgeNormal) is cut away.
+template <class VERTEX_ARRAY>
+void ClipPolyVsEdge(const VERTEX_ARRAY &inPolygonToClip, Vec3Arg inEdgeVertex1, Vec3Arg inEdgeVertex2, Vec3Arg inClippingEdgeNormal, VERTEX_ARRAY &outClippedPolygon)
+{
+	JPH_ASSERT(inPolygonToClip.size() >= 3);
+	JPH_ASSERT(outClippedPolygon.empty());
+
+	// Get normal that is perpendicular to the edge and the clipping edge normal
+	Vec3 edge = inEdgeVertex2 - inEdgeVertex1;
+	Vec3 edge_normal = inClippingEdgeNormal.Cross(edge);
+
+	// Project vertices of edge on inPolygonToClip
+	Vec3 polygon_normal = (inPolygonToClip[2] - inPolygonToClip[0]).Cross(inPolygonToClip[1] - inPolygonToClip[0]);
+	float polygon_normal_len_sq = polygon_normal.LengthSq();
+	Vec3 v1 = inEdgeVertex1 + polygon_normal.Dot(inPolygonToClip[0] - inEdgeVertex1) * polygon_normal / polygon_normal_len_sq;
+	Vec3 v2 = inEdgeVertex2 + polygon_normal.Dot(inPolygonToClip[0] - inEdgeVertex2) * polygon_normal / polygon_normal_len_sq;
+	Vec3 v12 = v2 - v1;
+	float v12_len_sq = v12.LengthSq();
+
+	// Determine state of last point
+	Vec3 e1 = inPolygonToClip[inPolygonToClip.size() - 1];
+	float prev_num = (inEdgeVertex1 - e1).Dot(edge_normal);
+	bool prev_inside = prev_num < 0.0f; 
+	
+	// Loop through all vertices
+	for (typename VERTEX_ARRAY::size_type j = 0; j < inPolygonToClip.size(); ++j)
+	{
+		// Check if second point is inside
+		Vec3 e2 = inPolygonToClip[j];
+		float num = (inEdgeVertex1 - e2).Dot(edge_normal);
+		bool cur_inside = num < 0.0f;
+
+		// In -> Out or Out -> In: Add point on clipping plane
+		if (cur_inside != prev_inside)
+		{
+			// Solve: (X - inPlaneOrigin) . inPlaneNormal = 0 and X = e1 + t * (e2 - e1) for X
+			Vec3 e12 = e2 - e1;
+			float denom = e12.Dot(edge_normal);
+			Vec3 clipped_point = e1 + (prev_num / denom) * e12;
+
+			// Project point on line segment v1, v2 so see if it falls outside if the edge
+			float projection = (clipped_point - v1).Dot(v12);
+			if (projection < 0.0f)
+				outClippedPolygon.push_back(v1);
+			else if (projection > v12_len_sq)
+				outClippedPolygon.push_back(v2);
+			else
+				outClippedPolygon.push_back(clipped_point);
+		}
+		
+		// Update previous state
+		prev_num = num;
+		prev_inside = cur_inside;
+		e1 = e2;
+	}
+}
+
+/// Clip polygon vs axis aligned box, inPolygonToClip is assume to be in counter clockwise order.
+/// Output will be stored in outClippedPolygon. Everything inside inAABox will be kept.
+template <class VERTEX_ARRAY>
+void ClipPolyVsAABox(const VERTEX_ARRAY &inPolygonToClip, const AABox &inAABox, VERTEX_ARRAY &outClippedPolygon)
+{
+	JPH_ASSERT(inPolygonToClip.size() >= 2);
+
+	VERTEX_ARRAY tmp_vertices[2];
+	int tmp_vertices_idx = 0;
+	
+	for (int coord = 0; coord < 3; ++coord)
+		for (int side = 0; side < 2; ++side)
+		{
+			// Get plane to clip against
+			Vec3 origin = Vec3::sZero(), normal = Vec3::sZero();
+			if (side == 0)
+			{
+				normal.SetComponent(coord, 1.0f);
+				origin.SetComponent(coord, inAABox.mMin[coord]);
+			}
+			else
+			{
+				normal.SetComponent(coord, -1.0f);
+				origin.SetComponent(coord, inAABox.mMax[coord]);
+			}
+
+			// Get source and target polygon
+			const VERTEX_ARRAY &src_polygon = tmp_vertices_idx == 0? inPolygonToClip : tmp_vertices[(tmp_vertices_idx & 1)];
+			tmp_vertices_idx++;
+			VERTEX_ARRAY &tgt_polygon = tmp_vertices_idx == 6? outClippedPolygon : tmp_vertices[(tmp_vertices_idx & 1)];
+			tgt_polygon.clear();
+
+			// Clip against the edge
+			ClipPolyVsPlane(src_polygon, origin, normal, tgt_polygon);
+
+			// Break out if no polygon left
+			if (tgt_polygon.size() < 3)
+			{
+				outClippedPolygon.clear();
+				return;
+			}
+
+			// Flip normal
+			normal = -normal;
+		}
+}
+
+} // JPH

+ 423 - 0
Jolt/Geometry/ClosestPoint.h

@@ -0,0 +1,423 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace JPH {
+
+// Turn off fused multiply add instruction because it makes the equations of the form a * b - c * d inaccurate below
+JPH_PRECISE_MATH_ON
+
+/// Helper utils to find the closest point to a line segment, triangle or tetrahedron
+namespace ClosestPoint
+{
+	/// Compute barycentric coordinates of closest point to origin for infinite line defined by (inA, inB)
+	/// Point can then be computed as inA * outU + inB * outV
+	inline void GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, float &outU, float &outV)
+	{
+		Vec3 ab = inB - inA;
+		float denominator = ab.LengthSq();
+		if (denominator < Square(FLT_EPSILON))
+		{
+			// Degenerate line segment, fallback to points
+			if (inA.LengthSq() < inB.LengthSq())
+			{
+				// A closest
+				outU = 1.0f;
+				outV = 0.0f;
+			}
+			else
+			{
+				// B closest
+				outU = 0.0f;
+				outV = 1.0f;
+			}
+		}
+		else
+		{
+			outV = -inA.Dot(ab) / denominator;
+			outU = 1.0f - outV;
+		}
+	}
+	
+	/// Compute barycentric coordinates of closest point to origin for plane defined by (inA, inB, inC)
+	/// Point can then be computed as inA * outU + inB * outV + inC * outW
+	inline void GetBaryCentricCoordinates(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, float &outU, float &outV, float &outW)
+	{
+		// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Barycentric Coordinates)
+		// With p = 0
+		// Adjusted to always include the shortest edge of the triangle in the calculation to improve numerical accuracy
+
+		// First calculate the three edges
+		Vec3 v0 = inB - inA;
+		Vec3 v1 = inC - inA;
+		Vec3 v2 = inC - inB;
+
+		// Make sure that the shortest edge is included in the calculation to keep the products a * b - c * d as small as possible to preserve accuracy
+		float d00 = v0.Dot(v0); 
+		float d11 = v1.Dot(v1); 
+		float d22 = v2.Dot(v2);
+		if (d00 <= d22)
+		{
+			// Use v0 and v1 to calculate barycentric coordinates
+			float d01 = v0.Dot(v1); 
+		
+			float denominator = d00 * d11 - d01 * d01; 
+			if (abs(denominator) < FLT_EPSILON)
+			{
+				// Degenerate triangle, return coordinates along longest edge
+				if (d00 > d11)
+				{
+					GetBaryCentricCoordinates(inA, inB, outU, outV);
+					outW = 0.0f;
+				}
+				else
+				{
+					GetBaryCentricCoordinates(inA, inC, outU, outW);
+					outV = 0.0f;
+				}
+			}
+			else
+			{
+				float a0 = inA.Dot(v0);
+				float a1 = inA.Dot(v1); 
+				outV = (d01 * a1 - d11 * a0) / denominator; 
+				outW = (d01 * a0 - d00 * a1) / denominator; 
+				outU = 1.0f - outV - outW;
+			}
+		}
+		else
+		{
+			// Use v1 and v2 to calculate barycentric coordinates
+			float d12 = v1.Dot(v2); 
+		
+			float denominator = d11 * d22 - d12 * d12; 
+			if (abs(denominator) < FLT_EPSILON)
+			{
+				// Degenerate triangle, return coordinates along longest edge
+				if (d11 > d22)
+				{
+					GetBaryCentricCoordinates(inA, inC, outU, outW);
+					outV = 0.0f;
+				}
+				else
+				{
+					GetBaryCentricCoordinates(inB, inC, outV, outW);
+					outU = 0.0f;
+				}
+			}
+			else
+			{
+				float c1 = inC.Dot(v1);
+				float c2 = inC.Dot(v2); 
+				outU = (d22 * c1 - d12 * c2) / denominator; 
+				outV = (d11 * c2 - d12 * c1) / denominator; 
+				outW = 1.0f - outU - outV;
+			}
+		}
+	}
+
+	/// Get the closest point to the origin of line (inA, inB)
+	/// outSet describes which features are closest: 1 = a, 2 = b, 3 = line segment ab
+	inline Vec3	GetClosestPointOnLine(Vec3Arg inA, Vec3Arg inB, uint32 &outSet) 
+	{
+		float u, v;
+		GetBaryCentricCoordinates(inA, inB, u, v);
+		if (v <= 0.0f)
+		{
+			// inA is closest point
+			outSet = 0b0001;
+			return inA;
+		}
+		else if (u <= 0.0f)
+		{
+			// inB is closest point
+			outSet = 0b0010;
+			return inB;
+		}
+		else
+		{
+			// Closest point lies on line inA inB
+			outSet = 0b0011;
+			return u * inA + v * inB;
+		}
+	}
+
+	/// Get the closest point to the origin of triangle (inA, inB, inC)
+	/// outSet describes which features are closest: 1 = a, 2 = b, 4 = c, 5 = line segment ac, 7 = triangle interior etc.
+	inline Vec3	GetClosestPointOnTriangle(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, uint32 &outSet)
+	{
+		// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Triangle to Point)
+		// With p = 0
+
+		// Calculate edges
+		Vec3 ab = inB - inA; 
+		Vec3 ac = inC - inA; 
+		Vec3 bc = inC - inB;
+
+		// The most accurate normal is calculated by using the two shortest edges
+		// See: https://box2d.org/posts/2014/01/troublesome-triangle/
+		// The difference in normals is most pronounced when one edge is much smaller than the others (in which case the other 2 must have roughly the same length).
+		// Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal.
+		// We first check which of the edges is shorter
+		UVec4 bc_shorter_than_ac = Vec4::sLess(bc.DotV4(bc), ac.DotV4(ac));
+
+		// We calculate both normals and then select the one that had the shortest edge for our normal (this avoids branching)
+		Vec3 normal_bc = ab.Cross(bc);
+		Vec3 normal_ac = ab.Cross(ac);
+		Vec3 n = Vec3::sSelect(normal_ac, normal_bc, bc_shorter_than_ac);
+		float n_len_sq = n.LengthSq();
+
+		// Check degenerate
+		if (n_len_sq < Square(FLT_EPSILON))
+		{
+			// Degenerate, fallback to edges
+
+			// Edge AB
+			uint32 closest_set;
+			Vec3 closest_point = GetClosestPointOnLine(inA, inB, closest_set);
+			float best_dist_sq = closest_point.LengthSq();
+
+			// Edge AC
+			uint32 set;
+			Vec3 q = GetClosestPointOnLine(inA, inC, set);
+			float dist_sq = q.LengthSq();
+			if (dist_sq < best_dist_sq)
+			{
+				closest_point = q;
+				best_dist_sq = dist_sq;
+				closest_set = (set & 0b0001) + ((set & 0b0010) << 1);
+			}
+
+			// Edge BC
+			q = GetClosestPointOnLine(inB, inC, set);
+			dist_sq = q.LengthSq();
+			if (dist_sq < best_dist_sq)
+			{
+				closest_point = q;
+				best_dist_sq = dist_sq;
+				closest_set = set << 1;
+			}
+
+			outSet = closest_set;
+			return closest_point;
+		}
+
+		// Check if P in vertex region outside A 
+		Vec3 ap = -inA; 
+		float d1 = ab.Dot(ap); 
+		float d2 = ac.Dot(ap); 
+		if (d1 <= 0.0f && d2 <= 0.0f)
+		{
+			outSet = 0b0001;
+			return inA; // barycentric coordinates (1,0,0)
+		}
+
+		// Check if P in vertex region outside B 
+		Vec3 bp = -inB; 
+		float d3 = ab.Dot(bp); 
+		float d4 = ac.Dot(bp); 
+		if (d3 >= 0.0f && d4 <= d3) 
+		{
+			outSet = 0b0010;
+			return inB; // barycentric coordinates (0,1,0)
+		}
+
+		// Check if P in edge region of AB, if so return projection of P onto AB 
+		float vc = d1 * d4 - d3 * d2; 
+		if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) 
+		{ 
+			float v = d1 / (d1 - d3); 
+			outSet = 0b0011;
+			return inA + v * ab; // barycentric coordinates (1-v,v,0) 
+		}
+
+		// Check if P in vertex region outside C 
+		Vec3 cp = -inC; 
+		float d5 = ab.Dot(cp); 
+		float d6 = ac.Dot(cp); 
+		if (d6 >= 0.0f && d5 <= d6) 
+		{
+			outSet = 0b0100;
+			return inC; // barycentric coordinates (0,0,1)
+		}
+
+		// Check if P in edge region of AC, if so return projection of P onto AC 
+		float vb = d5 * d2 - d1 * d6; 
+		if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) 
+		{ 
+			float w = d2 / (d2 - d6); 
+			outSet = 0b0101;
+			return inA + w * ac; // barycentric coordinates (1-w,0,w) 
+		}
+
+		// Check if P in edge region of BC, if so return projection of P onto BC 
+		float va = d3 * d6 - d5 * d4;
+		float d4_d3 = d4 - d3;
+		float d5_d6 = d5 - d6;
+		if (va <= 0.0f && d4_d3 >= 0.0f && d5_d6 >= 0.0f) 
+		{ 
+			float w = d4_d3 / (d4_d3 + d5_d6); 
+			outSet = 0b0110;
+			return inB + w * bc; // barycentric coordinates (0,1-w,w) 
+		}
+
+		// P inside face region.
+		// Here we deviate from Christer Ericson's article to improve accuracy.
+		// Determine distance between triangle and origin: distance = (centroid - origin) . normal / |normal|
+		// Closest point to origin is then: distance . normal / |normal|
+		// Note that this way of calculating the closest point is much more accurate than first calculating barycentric coordinates 
+		// and then calculating the closest point based on those coordinates.
+		outSet = 0b0111;
+		return n * (inA + inB + inC).Dot(n) / (3.0f * n_len_sq);
+	}
+
+	/// Check if the origin is outside the plane of triangle (inA, inB, inC). inD specifies the front side of the plane.
+	inline bool OriginOutsideOfPlane(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD)
+	{
+		// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point)
+		// With p = 0
+
+		// Test if point p and d lie on opposite sides of plane through abc 
+		Vec3 n = (inB - inA).Cross(inC - inA);
+		float signp = inA.Dot(n); // [AP AB AC]
+		float signd = (inD - inA).Dot(n); // [AD AB AC] 
+													   
+		// Points on opposite sides if expression signs are the same
+		// Note that we left out the minus sign in signp so we need to check > 0 instead of < 0 as in Christer's book
+		// We compare against a small negative value to allow for a little bit of slop in the calculations
+		return signp * signd > -FLT_EPSILON; 
+	}
+
+	/// Returns for each of the planes of the tetrahedron if the origin is inside it
+	/// Roughly equivalent to: 
+	///	[OriginOutsideOfPlane(inA, inB, inC, inD), 
+	///	 OriginOutsideOfPlane(inA, inC, inD, inB), 
+	///	 OriginOutsideOfPlane(inA, inD, inB, inC), 
+	///	 OriginOutsideOfPlane(inB, inD, inC, inA)]
+	inline UVec4 OriginOutsideOfTetrahedronPlanes(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD)
+	{
+		Vec3 ab = inB - inA;
+		Vec3 ac = inC - inA;
+		Vec3 ad = inD - inA;
+		Vec3 bd = inD - inB;
+		Vec3 bc = inC - inB;
+
+		Vec3 ab_cross_ac = ab.Cross(ac);
+		Vec3 ac_cross_ad = ac.Cross(ad);
+		Vec3 ad_cross_ab = ad.Cross(ab);
+		Vec3 bd_cross_bc = bd.Cross(bc);
+		
+		// For each plane get the side on which the origin is
+		float signp0 = inA.Dot(ab_cross_ac); // ABC
+		float signp1 = inA.Dot(ac_cross_ad); // ACD
+		float signp2 = inA.Dot(ad_cross_ab); // ADB
+		float signp3 = inB.Dot(bd_cross_bc); // BDC
+		Vec4 signp(signp0, signp1, signp2, signp3);
+
+		// For each plane get the side that is outside (determined by the 4th point)
+		float signd0 = ad.Dot(ab_cross_ac);  // D
+		float signd1 = ab.Dot(ac_cross_ad);  // B
+		float signd2 = ac.Dot(ad_cross_ab);  // C
+		float signd3 = -ab.Dot(bd_cross_bc); // A
+		Vec4 signd(signd0, signd1, signd2, signd3);
+
+		// The winding of all triangles has been chosen so that signd should have the
+		// same sign for all components. If this is not the case the tetrahedron
+		// is degenerate and we return that the origin is in front of all sides
+		int sign_bits = signd.GetSignBits();
+		switch (sign_bits)
+		{
+		case 0:
+			// All positive
+			return Vec4::sGreaterOrEqual(signp, Vec4::sReplicate(-FLT_EPSILON));
+
+		case 0xf:
+			// All negative
+			return Vec4::sLessOrEqual(signp, Vec4::sReplicate(FLT_EPSILON));
+
+		default:
+			// Mixed signs, degenerate tetrahedron
+			return UVec4::sReplicate(0xffffffff);
+		}
+	}
+
+	/// Get the closest point between tetrahedron (inA, inB, inC, inD) to the origin
+	/// outSet specifies which feature was closest, 1 = a, 2 = b, 4 = c, 8 = d. Edges have 2 bits set, triangles 3 and if the point is in the interior 4 bits are set.
+	inline Vec3	GetClosestPointOnTetrahedron(Vec3Arg inA, Vec3Arg inB, Vec3Arg inC, Vec3Arg inD, uint32 &outSet)
+	{
+		// Taken from: Real-Time Collision Detection - Christer Ericson (Section: Closest Point on Tetrahedron to Point)
+		// With p = 0
+
+		// Start out assuming point inside all halfspaces, so closest to itself 
+		uint32 closest_set = 0b1111;
+		Vec3 closest_point = Vec3::sZero(); 
+		float best_dist_sq = FLT_MAX; 
+		
+		// Determine for each of the faces of the tetrahedron if the origin is in front of the plane
+		UVec4 origin_out_of_planes = OriginOutsideOfTetrahedronPlanes(inA, inB, inC, inD);
+
+		// If point outside face abc then compute closest point on abc 
+		if (origin_out_of_planes.GetX()) // OriginOutsideOfPlane(inA, inB, inC, inD)
+		{ 
+			Vec3 q = GetClosestPointOnTriangle(inA, inB, inC, closest_set); 
+			float dist_sq = q.LengthSq(); 
+			
+			// Update best closest point if (squared) distance is less than current best 
+			if (dist_sq < best_dist_sq) 
+			{
+				best_dist_sq = dist_sq;
+				closest_point = q; 
+			}
+		} 
+		
+		// Repeat test for face acd 
+		if (origin_out_of_planes.GetY()) // OriginOutsideOfPlane(inA, inC, inD, inB)
+		{ 
+			uint32 set;
+			Vec3 q = GetClosestPointOnTriangle(inA, inC, inD, set); 
+			float dist_sq = q.LengthSq(); 
+			if (dist_sq < best_dist_sq) 
+			{
+				best_dist_sq = dist_sq;
+				closest_point = q;
+				closest_set = (set & 0b0001) + ((set & 0b0110) << 1);
+			}
+		}
+
+		// Repeat test for face adb 
+		if (origin_out_of_planes.GetZ()) // OriginOutsideOfPlane(inA, inD, inB, inC)
+		{
+			uint32 set;
+			Vec3 q = GetClosestPointOnTriangle(inA, inD, inB, set); 
+			float dist_sq = q.LengthSq(); 
+			if (dist_sq < best_dist_sq) 
+			{
+				best_dist_sq = dist_sq;
+				closest_point = q;
+				closest_set = (set & 0b0001) + ((set & 0b0010) << 2) + ((set & 0b0100) >> 1); 
+			}
+		} 
+		
+		// Repeat test for face bdc 
+		if (origin_out_of_planes.GetW()) // OriginOutsideOfPlane(inB, inD, inC, inA)
+		{ 
+			uint32 set;
+			Vec3 q = GetClosestPointOnTriangle(inB, inD, inC, set); 
+			float dist_sq = q.LengthSq(); 
+			if (dist_sq < best_dist_sq) 
+			{
+				best_dist_sq = dist_sq;
+				closest_point = q;
+				closest_set = ((set & 0b0001) << 1) + ((set & 0b0010) << 2) + (set & 0b0100); 
+			}
+		} 
+	
+		outSet = closest_set;
+		return closest_point;
+	}
+};
+
+JPH_PRECISE_MATH_OFF
+
+} // JPH

+ 1375 - 0
Jolt/Geometry/ConvexHullBuilder.cpp

@@ -0,0 +1,1375 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Geometry/ConvexHullBuilder.h>
+#include <Geometry/ConvexHullBuilder2D.h>
+#include <Geometry/ClosestPoint.h>
+#include <Core/StringTools.h>
+#include <unordered_set>
+#include <fstream>
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	#include <Renderer/DebugRenderer.h>
+#endif
+
+namespace JPH {
+
+ConvexHullBuilder::Face::~Face()
+{
+	// Free all edges
+	Edge *e = mFirstEdge;
+	if (e != nullptr)
+	{
+		do 
+		{ 
+			Edge *next = e->mNextEdge;
+			delete e;
+			e = next;
+		} while (e != mFirstEdge);
+	}
+}
+
+void ConvexHullBuilder::Face::CalculateNormalAndCentroid(const Vec3 *inPositions)
+{
+	// Get point that we use to construct a triangle fan
+	Edge *e = mFirstEdge;
+	Vec3 y0 = inPositions[e->mStartIdx];
+
+	// Get the 2nd point
+	e = e->mNextEdge;
+	Vec3 y1 = inPositions[e->mStartIdx];
+
+	// Start accumulating the centroid
+	mCentroid = y0 + y1;
+	int n = 2;
+
+	// Start accumulating the normal
+	mNormal = Vec3::sZero();
+
+	// Loop over remaining edges accumulating normals in a triangle fan fashion
+	for (e = e->mNextEdge; e != mFirstEdge; e = e->mNextEdge)
+	{
+		// Get the 3rd point
+		Vec3 y2 = inPositions[e->mStartIdx];
+
+		// Calculate edges (counter clockwise)
+		Vec3 e0 = y1 - y0;
+		Vec3 e1 = y2 - y1;
+		Vec3 e2 = y0 - y2;
+
+		// The best normal is calculated by using the two shortest edges
+		// See: https://box2d.org/posts/2014/01/troublesome-triangle/
+		// The difference in normals is most pronounced when one edge is much smaller than the others (in which case the others must have roughly the same length).
+		// Therefore we can suffice by just picking the shortest from 2 edges and use that with the 3rd edge to calculate the normal.
+		// We first check which of the edges is shorter: e1 or e2
+		UVec4 e1_shorter_than_e2 = Vec4::sLess(e1.DotV4(e1), e2.DotV4(e2));
+
+		// We calculate both normals and then select the one that had the shortest edge for our normal (this avoids branching)
+		Vec3 normal_e01 = e0.Cross(e1);
+		Vec3 normal_e02 = e2.Cross(e0);
+		mNormal += Vec3::sSelect(normal_e02, normal_e01, e1_shorter_than_e2);
+
+		// Accumulate centroid
+		mCentroid += y2;
+		n++;
+
+		// Update y1 for next triangle
+		y1 = y2;
+	}
+
+	// Finalize centroid
+	mCentroid /= float(n);
+}
+
+void ConvexHullBuilder::Face::Initialize(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions)
+{
+	JPH_ASSERT(mFirstEdge == nullptr);
+	JPH_ASSERT(inIdx0 != inIdx1 && inIdx0 != inIdx2 && inIdx1 != inIdx2);
+
+	// Create 3 edges
+	Edge *e0 = new Edge(this, inIdx0);
+	Edge *e1 = new Edge(this, inIdx1);
+	Edge *e2 = new Edge(this, inIdx2);
+
+	// Link edges
+	e0->mNextEdge = e1;
+	e1->mNextEdge = e2;
+	e2->mNextEdge = e0;
+	mFirstEdge = e0;
+
+	CalculateNormalAndCentroid(inPositions);
+}
+
+ConvexHullBuilder::ConvexHullBuilder(const Positions &inPositions) :
+	mPositions(inPositions)
+{
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	mIteration = 0;
+
+	// Center the drawing of the first hull around the origin and calculate the delta offset between states
+	mOffset = Vec3::sZero();
+	if (mPositions.empty())
+	{
+		// No hull will be generated
+		mDelta = Vec3::sZero();
+	}
+	else
+	{
+		Vec3 maxv = Vec3::sReplicate(-FLT_MAX), minv = Vec3::sReplicate(FLT_MAX);
+		for (Vec3 v : mPositions)
+		{
+			minv = Vec3::sMin(minv, v);
+			maxv = Vec3::sMax(maxv, v);
+			mOffset -= v;
+		}
+		mOffset /= float(mPositions.size());
+		mDelta = Vec3((maxv - minv).GetX() + 0.5f, 0, 0);
+		mOffset += mDelta; // Don't start at origin, we're already drawing the final hull there
+	}
+#endif
+}
+
+void ConvexHullBuilder::FreeFaces()
+{
+	for (Face *f : mFaces)
+		delete f;
+	mFaces.clear();
+}
+
+void ConvexHullBuilder::AssignPointToFace(int inPositionIdx, const Faces &inFaces) const
+{
+	Vec3 point = mPositions[inPositionIdx];
+
+	Face *best_face = nullptr;
+	float best_dist_sq = 0.0f;
+
+	// Test against all faces
+	for (Face *f : inFaces)
+		if (!f->mRemoved)
+		{
+			// Determine distance to face
+			float dot = f->mNormal.Dot(point - f->mCentroid);
+			if (dot > 0.0f)
+			{
+				float dist_sq = dot * dot / f->mNormal.LengthSq();
+				if (dist_sq > best_dist_sq)
+				{
+					best_face = f;
+					best_dist_sq = dist_sq;
+				}
+			}
+		}
+
+	// If this point is in front of the face, add it to the conflict list
+	if (best_face != nullptr)
+	{
+		if (best_dist_sq > best_face->mFurthestPointDistanceSq)
+		{
+			// This point is futher away than any others, update the distance and add point as last point
+			best_face->mFurthestPointDistanceSq = best_dist_sq;
+			best_face->mConflictList.push_back(inPositionIdx);
+		}
+		else
+		{
+			// Not the furthest point, add it as the before last point
+			best_face->mConflictList.push_back(best_face->mConflictList.back());
+			best_face->mConflictList[best_face->mConflictList.size() - 2] = inPositionIdx;
+		}
+	}
+}
+
+float ConvexHullBuilder::DetermineCoplanarDistance() const
+{
+	// Formula as per: Implementing Quickhull - Dirk Gregorius.
+	Vec3 vmax = Vec3::sZero();
+	for (Vec3 v : mPositions)
+		vmax = Vec3::sMax(vmax, v.Abs());
+	return 3.0f * FLT_EPSILON * (vmax.GetX() + vmax.GetY() + vmax.GetZ());
+}
+
+int ConvexHullBuilder::GetNumVerticesUsed() const
+{
+	unordered_set<int> used_verts;
+	for (Face *f : mFaces)
+	{
+		Edge *e = f->mFirstEdge;
+		do 
+		{ 
+			used_verts.insert(e->mStartIdx);
+			e = e->mNextEdge;
+		} while (e != f->mFirstEdge);
+	}
+	return (int)used_verts.size();
+}
+
+bool ConvexHullBuilder::ContainsFace(const vector<int> &inIndices)
+{
+	for (Face *f : mFaces)
+	{
+		Edge *e = f->mFirstEdge;
+		vector<int>::const_iterator index = find(inIndices.begin(), inIndices.end(), e->mStartIdx);
+		if (index != inIndices.end())
+		{
+			size_t matches = 0;
+
+			do 
+			{ 
+				// Check if index matches
+				if (*index != e->mStartIdx)
+					break;
+
+				// Increment number of matches
+				matches++;
+				
+				// Next index in list of inIndices
+				index++;
+				if (index == inIndices.end())
+					index = inIndices.begin();
+
+				// Next edge
+				e = e->mNextEdge;
+			} while (e != f->mFirstEdge);
+			
+			if (matches == inIndices.size())
+				return true;
+		}
+	}
+
+	return false;
+}
+
+ConvexHullBuilder::EResult ConvexHullBuilder::Initialize(int inMaxVertices, float inTolerance, string &outError)
+{
+	// Free the faces possibly left over from an earlier hull
+	FreeFaces();
+
+	// Test that we have at least 3 points
+	if (mPositions.size() < 3)
+	{
+		outError = "Need at least 3 points to make a hull";
+		return EResult::TooFewPoints;
+	}
+
+	// Determine a suitable tolerance for detecting that points are coplanar
+	float coplanar_tolerance_sq = Square(DetermineCoplanarDistance());
+
+	// Increase desired tolerance if accuracy doesn't allow it
+	float tolerance_sq = max(coplanar_tolerance_sq, Square(inTolerance));
+	
+	// Find point furthest from the origin
+	int idx1 = -1;
+	float max_dist_sq = -1.0f;
+	for (int i = 0; i < (int)mPositions.size(); ++i)
+	{
+		float dist_sq = mPositions[i].LengthSq();
+		if (dist_sq > max_dist_sq)
+		{
+			max_dist_sq = dist_sq;
+			idx1 = i;
+		}
+	}
+	JPH_ASSERT(idx1 >= 0);
+
+	// Find point that is furthest away from this point
+	int idx2 = -1;
+	max_dist_sq = -1.0f;
+	for (int i = 0; i < (int)mPositions.size(); ++i)
+		if (i != idx1)
+		{
+			float dist_sq = (mPositions[i] - mPositions[idx1]).LengthSq();
+			if (dist_sq > max_dist_sq)
+			{
+				max_dist_sq = dist_sq;
+				idx2 = i;
+			}
+		}
+	JPH_ASSERT(idx2 >= 0);
+
+	// Find point that forms the biggest triangle
+	int idx3 = -1;
+	float best_triangle_area_sq = -1.0f;
+	for (int i = 0; i < (int)mPositions.size(); ++i)
+		if (i != idx1 && i != idx2)
+		{
+			float triangle_area_sq = (mPositions[idx1] - mPositions[i]).Cross(mPositions[idx2] - mPositions[i]).LengthSq();
+			if (triangle_area_sq > best_triangle_area_sq)
+			{
+				best_triangle_area_sq = triangle_area_sq;
+				idx3 = i;
+			}
+		}
+	JPH_ASSERT(idx3 >= 0);
+	if (best_triangle_area_sq < FLT_EPSILON)
+	{
+		outError = "Could not find a suitable initial triangle because its area was too small";
+		return EResult::Degenerate;
+	}
+
+	// Check if we have only 3 vertices
+	if (mPositions.size() == 3)
+	{
+		// Create two triangles (back to back)
+		Face *t1 = CreateTriangle(idx1, idx2, idx3);
+		Face *t2 = CreateTriangle(idx1, idx3, idx2);
+
+		// Link faces edges
+		LinkFace(t1->mFirstEdge, t2->mFirstEdge->mNextEdge->mNextEdge);
+		LinkFace(t1->mFirstEdge->mNextEdge, t2->mFirstEdge->mNextEdge);
+		LinkFace(t1->mFirstEdge->mNextEdge->mNextEdge, t2->mFirstEdge);
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+		// Draw current state
+		DrawState();
+#endif
+
+		return EResult::Success;
+	}
+
+	// Find point that forms the biggest tetrahedron
+	Vec3 initial_plane_normal = (mPositions[idx2] - mPositions[idx1]).Cross(mPositions[idx3] - mPositions[idx1]).Normalized();
+	Vec3 initial_plane_centroid = (mPositions[idx1] + mPositions[idx2] + mPositions[idx3]) / 3.0f;
+	int idx4 = -1;
+	float max_dist = 0.0f;
+	for (int i = 0; i < (int)mPositions.size(); ++i)
+		if (i != idx1 && i != idx2 && i != idx3)
+		{
+			float dist = (mPositions[i] - initial_plane_centroid).Dot(initial_plane_normal);
+			if (abs(dist) > abs(max_dist))
+			{
+				max_dist = dist;
+				idx4 = i;
+			}
+		}
+
+	// Check if the hull is coplanar
+	if (Square(max_dist) <= 25.0f * coplanar_tolerance_sq)
+	{
+		// First project all points in 2D space
+		Vec3 base1 = initial_plane_normal.GetNormalizedPerpendicular();
+		Vec3 base2 = initial_plane_normal.Cross(base1);
+		vector<Vec3> positions_2d;
+		positions_2d.reserve(mPositions.size());
+		for (Vec3 v : mPositions)
+			positions_2d.push_back(Vec3(base1.Dot(v), base2.Dot(v), 0));
+
+		// Build hull
+		vector<int> edges_2d;
+		ConvexHullBuilder2D builder_2d(positions_2d);
+		ConvexHullBuilder2D::EResult result = builder_2d.Initialize(idx1, idx2, idx3, inMaxVertices, inTolerance, edges_2d);
+
+		// Create faces (back to back)
+		Face *f1 = CreateFace();
+		Face *f2 = CreateFace();
+
+		// Create edges for face 1
+		vector<Edge *> edges_f1;
+		edges_f1.reserve(edges_2d.size());
+		for (int i = 0; i < (int)edges_2d.size(); ++i)
+		{
+			Edge *edge = new Edge(f1, edges_2d[i]);
+			if (edges_f1.empty())
+				f1->mFirstEdge = edge;
+			else
+				edges_f1.back()->mNextEdge = edge;
+			edges_f1.push_back(edge);
+		}
+		edges_f1.back()->mNextEdge = f1->mFirstEdge;
+
+		// Create edges for face 2
+		vector<Edge *> edges_f2;
+		edges_f2.reserve(edges_2d.size());
+		for (int i = (int)edges_2d.size() - 1; i >= 0; --i)
+		{
+			Edge *edge = new Edge(f2, edges_2d[i]);
+			if (edges_f2.empty())
+				f2->mFirstEdge = edge;
+			else
+				edges_f2.back()->mNextEdge = edge;
+			edges_f2.push_back(edge);
+		}
+		edges_f2.back()->mNextEdge = f2->mFirstEdge;
+
+		// Link edges
+		for (size_t i = 0; i < edges_2d.size(); ++i)
+			LinkFace(edges_f1[i], edges_f2[(2 * edges_2d.size() - 2 - i) % edges_2d.size()]);
+
+		// Calculate the plane for both faces
+		f1->CalculateNormalAndCentroid(mPositions.data());
+		f2->mNormal = -f1->mNormal;
+		f2->mCentroid = f1->mCentroid;
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+		// Draw current state
+		DrawState();
+#endif
+
+		return result == ConvexHullBuilder2D::EResult::MaxVerticesReached? EResult::MaxVerticesReached : EResult::Success;
+	}
+
+	// Ensure the planes are facing outwards
+	if (max_dist < 0.0f)
+		swap(idx2, idx3);
+
+	// Create tetrahedron
+	Face *t1 = CreateTriangle(idx1, idx2, idx4);
+	Face *t2 = CreateTriangle(idx2, idx3, idx4);
+	Face *t3 = CreateTriangle(idx3, idx1, idx4);
+	Face *t4 = CreateTriangle(idx1, idx3, idx2);
+
+	// Link face edges
+	LinkFace(t1->mFirstEdge, t4->mFirstEdge->mNextEdge->mNextEdge);
+	LinkFace(t1->mFirstEdge->mNextEdge, t2->mFirstEdge->mNextEdge->mNextEdge);
+	LinkFace(t1->mFirstEdge->mNextEdge->mNextEdge, t3->mFirstEdge->mNextEdge);
+	LinkFace(t2->mFirstEdge, t4->mFirstEdge->mNextEdge);
+	LinkFace(t2->mFirstEdge->mNextEdge, t3->mFirstEdge->mNextEdge->mNextEdge);
+	LinkFace(t3->mFirstEdge, t4->mFirstEdge);
+
+	// Build the initial conflict lists
+	Faces faces { t1, t2, t3, t4 };
+	for (int idx = 0; idx < (int)mPositions.size(); ++idx)
+		if (idx != idx1 && idx != idx2 && idx != idx3 && idx != idx4)
+			AssignPointToFace(idx, faces);
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	// Draw current state including conflict list
+	DrawState(true);
+
+	// Increment iteration counter
+	++mIteration;
+#endif
+
+	// Overestimate of the actual amount of vertices we use, for limiting the amount of vertices in the hull
+	int num_vertices_used = 4;
+
+	// Loop through the remainder of the points and add them
+	for (;;)
+	{
+		// Find the face with the furthest point on it
+		Face *face_with_furthest_point = nullptr;
+		float furthest_dist_sq = 0.0f;
+		for (Face *f : mFaces)
+			if (f->mFurthestPointDistanceSq > furthest_dist_sq)
+			{
+				furthest_dist_sq = f->mFurthestPointDistanceSq;
+				face_with_furthest_point = f;
+			}
+
+		// If there is none closer than our tolerance value, we're done
+		if (face_with_furthest_point == nullptr || furthest_dist_sq < tolerance_sq)
+			break;
+
+		// Check if we have a limit on the max vertices that we should produce
+		if (num_vertices_used >= inMaxVertices)
+		{
+			// Count the actual amount of used vertices (we did not take the removal of any vertices into account)
+			num_vertices_used = GetNumVerticesUsed();
+
+			// Check if there are too many
+			if (num_vertices_used >= inMaxVertices)
+				return EResult::MaxVerticesReached;
+		}
+
+		// We're about to add another vertex
+		++num_vertices_used;
+
+		// Take the furthest point
+		int furthest_point_idx = face_with_furthest_point->mConflictList.back();
+		face_with_furthest_point->mConflictList.pop_back();
+
+		// Add the point to the hull
+		Faces new_faces;
+		AddPoint(face_with_furthest_point, furthest_point_idx, coplanar_tolerance_sq, new_faces);
+
+		// Redistribute points on conflict lists belonging to removed faces
+		for (Face *face : mFaces)
+			if (face->mRemoved)
+				for (int idx : face->mConflictList)
+					AssignPointToFace(idx, new_faces);
+
+		// Permanently delete faces that we removed in AddPoint()
+		GarbageCollectFaces();
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+		// Draw state at the end of this step including conflict list
+		DrawState(true);
+
+		// Increment iteration counter
+		++mIteration;
+#endif
+	}
+
+	return EResult::Success;
+}
+
+void ConvexHullBuilder::AddPoint(Face *inFacingFace, int inIdx, float inCoplanarToleranceSq, Faces &outNewFaces)
+{
+	// Get position
+	Vec3 pos = mPositions[inIdx];
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	// Draw point to be added
+	DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + pos), Color::sYellow, 0.1f);
+	DebugRenderer::sInstance->DrawText3D(cDrawScale * (mOffset + pos), ConvertToString(inIdx), Color::sWhite);
+#endif
+
+#ifdef JPH_ENABLE_ASSERTS
+	// Check if structure is intact
+	ValidateFaces();
+#endif
+
+	// Find edge of convex hull of faces that are not facing the new vertex
+	FullEdges edges;
+	FindEdge(inFacingFace, pos, edges);
+	JPH_ASSERT(edges.size() >= 3);
+
+	// Create new faces
+	outNewFaces.reserve(edges.size());
+	for (FullEdge &e : edges)
+	{
+		JPH_ASSERT(e.mStartIdx != e.mEndIdx);
+		Face *f = CreateTriangle(e.mStartIdx, e.mEndIdx, inIdx);
+		outNewFaces.push_back(f);
+	}
+		
+	// Link edges
+	for (Faces::size_type i = 0; i < outNewFaces.size(); ++i)
+	{
+		LinkFace(outNewFaces[i]->mFirstEdge, edges[i].mNeighbourEdge);
+		LinkFace(outNewFaces[i]->mFirstEdge->mNextEdge, outNewFaces[(i + 1) % outNewFaces.size()]->mFirstEdge->mNextEdge->mNextEdge);
+	}
+
+	// Loop on faces that were modified until nothing needs to be checked anymore
+	Faces affected_faces = outNewFaces;
+	while (!affected_faces.empty())
+	{
+		// Take the next face
+		Face *face = affected_faces.back();
+		affected_faces.pop_back();
+
+		if (!face->mRemoved)
+		{
+			// Merge with neighbour if this is a degenerate face
+			MergeDegenerateFace(face, 1.0e-12f, affected_faces);
+
+			// Merge with coplanar neighbours (or when the neighbour forms a concave edge)
+			if (!face->mRemoved)
+				MergeCoplanarOrConcaveFaces(face, inCoplanarToleranceSq, affected_faces);
+		}
+	}
+
+#ifdef JPH_ENABLE_ASSERTS
+	// Check if structure is intact
+	ValidateFaces();
+#endif
+}
+
+void ConvexHullBuilder::GarbageCollectFaces()
+{
+	for (int i = (int)mFaces.size() - 1; i >= 0; --i)
+	{
+		Face *f = mFaces[i];
+		if (f->mRemoved)
+		{
+			FreeFace(f);
+			mFaces.erase(mFaces.begin() + i);
+		}
+	}
+}
+
+ConvexHullBuilder::Face *ConvexHullBuilder::CreateFace()
+{
+	// Call provider to create face
+	Face *f = new Face();
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	// Remember iteration counter
+	f->mIteration = mIteration;
+#endif
+
+	// Add to list
+	mFaces.push_back(f);
+	return f;
+}
+
+ConvexHullBuilder::Face *ConvexHullBuilder::CreateTriangle(int inIdx1, int inIdx2, int inIdx3)
+{
+	Face *f = CreateFace();
+	f->Initialize(inIdx1, inIdx2, inIdx3, mPositions.data());
+	return f;
+}
+
+void ConvexHullBuilder::FreeFace(Face *inFace)
+{
+	JPH_ASSERT(inFace->mRemoved);
+
+#ifdef JPH_ENABLE_ASSERTS
+	// Make sure that this face is not connected
+	Edge *e = inFace->mFirstEdge;
+	if (e != nullptr)
+		do 
+		{ 
+			JPH_ASSERT(e->mNeighbourEdge == nullptr);
+			e = e->mNextEdge;
+		} while (e != inFace->mFirstEdge);
+#endif
+
+	// Free the face
+	delete inFace;
+}
+
+void ConvexHullBuilder::LinkFace(Edge *inEdge1, Edge *inEdge2)
+{
+	// Check not connected yet
+	JPH_ASSERT(inEdge1->mNeighbourEdge == nullptr);
+	JPH_ASSERT(inEdge2->mNeighbourEdge == nullptr);
+	JPH_ASSERT(inEdge1->mFace != inEdge2->mFace);
+
+	// Check vertices match
+	JPH_ASSERT(inEdge1->mStartIdx == inEdge2->mNextEdge->mStartIdx);
+	JPH_ASSERT(inEdge2->mStartIdx == inEdge1->mNextEdge->mStartIdx);
+
+	// Link up
+	inEdge1->mNeighbourEdge = inEdge2;
+	inEdge2->mNeighbourEdge = inEdge1;
+}
+
+void ConvexHullBuilder::UnlinkFace(Face *inFace)
+{
+	// Unlink from neighbours
+	Edge *e = inFace->mFirstEdge;
+	do 
+	{ 
+		if (e->mNeighbourEdge != nullptr)
+		{
+			// Validate that neighbour points to us
+			JPH_ASSERT(e->mNeighbourEdge->mNeighbourEdge == e);
+
+			// Unlink
+			e->mNeighbourEdge->mNeighbourEdge = nullptr;
+			e->mNeighbourEdge = nullptr;
+		}
+		e = e->mNextEdge;
+	} while (e != inFace->mFirstEdge);
+}
+
+void ConvexHullBuilder::FindEdge(Face *inFacingFace, Vec3Arg inVertex, FullEdges &outEdges)
+{
+	// Assert that we were given an empty array
+	JPH_ASSERT(outEdges.empty());
+
+	// Should start with a facing face
+	JPH_ASSERT(inFacingFace->IsFacing(inVertex));
+
+	// Flag as removed
+	inFacingFace->mRemoved = true;
+
+	// Instead of recursing, we build our own stack with the information we need
+	struct StackEntry
+	{
+		Edge *		mFirstEdge;
+		Edge *		mCurrentEdge;
+	};
+	constexpr int cMaxEdgeLength = 128;
+	StackEntry stack[cMaxEdgeLength];
+	int cur_stack_pos = 0;
+
+	static_assert(alignof(Edge) >= 2, "Need lowest bit to indicate to tell if we completed the loop");
+
+	// Start with the face / edge provided
+	stack[0].mFirstEdge = inFacingFace->mFirstEdge;
+	stack[0].mCurrentEdge = reinterpret_cast<Edge *>(reinterpret_cast<uintptr_t>(inFacingFace->mFirstEdge) | 1); // Set lowest bit of pointer to make it different from the first edge
+
+	for (;;)
+	{
+		StackEntry &cur_entry = stack[cur_stack_pos];
+
+		// Next edge
+		Edge *raw_e = cur_entry.mCurrentEdge;
+		Edge *e = reinterpret_cast<Edge *>(reinterpret_cast<uintptr_t>(raw_e) & ~uintptr_t(1)); // Remove the lowest bit which was used to indicate that this is the first edge we're testing
+		cur_entry.mCurrentEdge = e->mNextEdge;
+
+		// If we're back at the first edge we've completed the face and we're done
+		if (raw_e == cur_entry.mFirstEdge)
+		{
+			// This face needs to be removed, unlink it now, caller will free
+			UnlinkFace(e->mFace);
+
+			// Pop from stack
+			if (--cur_stack_pos < 0)
+				break;
+		}
+		else
+		{
+			// Visit neighbour face
+			Edge *ne = e->mNeighbourEdge;
+			if (ne != nullptr)
+			{
+				Face *n = ne->mFace;
+				if (!n->mRemoved)
+				{
+					// Check if vertex is on the front side of this face
+					if (n->IsFacing(inVertex))
+					{
+						// Vertex on front, this face needs to be removed
+						n->mRemoved = true;
+
+						// Add element to the stack of elements to visit
+						cur_stack_pos++;
+						JPH_ASSERT(cur_stack_pos < cMaxEdgeLength);
+						StackEntry &new_entry = stack[cur_stack_pos];
+						new_entry.mFirstEdge = ne;
+						new_entry.mCurrentEdge = ne->mNextEdge; // We don't need to test this edge again since we came from it
+					}
+					else
+					{
+						// Vertex behind, keep edge
+						FullEdge full;
+						full.mNeighbourEdge = ne;
+						full.mStartIdx = e->mStartIdx;
+						full.mEndIdx = ne->mStartIdx;
+						outEdges.push_back(full);
+					}
+				}
+			}
+		}
+	}
+
+	// Assert that we have a fully connected loop
+#ifdef JPH_ENABLE_ASSERTS
+	for (int i = 0; i < (int)outEdges.size(); ++i)
+		JPH_ASSERT(outEdges[i].mEndIdx == outEdges[(i + 1) % outEdges.size()].mStartIdx);
+#endif
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	// Draw edge of facing faces
+	for (int i = 0; i < (int)outEdges.size(); ++i)
+		DebugRenderer::sInstance->DrawArrow(cDrawScale * (mPositions[outEdges[i].mStartIdx] + mOffset), cDrawScale * (mPositions[outEdges[i].mEndIdx] + mOffset), Color::sWhite, 0.01f);
+	DrawState();
+#endif
+}
+
+void ConvexHullBuilder::MergeFaces(Edge *inEdge)
+{
+	// Get the face
+	Face *face = inEdge->mFace;
+
+	// Find the previous and next edge
+	Edge *next_edge = inEdge->mNextEdge;
+	Edge *prev_edge = inEdge->GetPreviousEdge();
+
+	// Get the other face
+	Edge *other_edge = inEdge->mNeighbourEdge;
+	Face *other_face = other_edge->mFace;
+
+	// Check if attempting to merge with self
+	JPH_ASSERT(face != other_face);
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	DrawWireFace(face, Color::sGreen);
+	DrawWireFace(other_face, Color::sRed);
+	DrawState();
+#endif
+
+	// Loop over the edges of the other face and make them belong to inFace
+	Edge *edge = other_edge->mNextEdge;
+	prev_edge->mNextEdge = edge;
+	for (;;)
+	{
+		edge->mFace = face;
+		if (edge->mNextEdge == other_edge)
+		{
+			// Terminate when we are back at other_edge
+			edge->mNextEdge = next_edge;
+			break;
+		}
+		edge = edge->mNextEdge;
+	}
+
+	// If the first edge happens to be inEdge we need to fix it because this edge is no longer part of the face.
+	// Note that we replace it with the first edge of the merged face so that if the MergeFace function is called
+	// from a loop that loops around the face that it will still terminate after visiting all edges once.
+	if (face->mFirstEdge == inEdge)
+		face->mFirstEdge = prev_edge->mNextEdge;
+
+	// Free the edges
+	delete inEdge;
+	delete other_edge;
+
+	// Mark the other face as removed
+	other_face->mFirstEdge = nullptr;
+	other_face->mRemoved = true;
+
+	// Recalculate plane
+	face->CalculateNormalAndCentroid(mPositions.data());
+
+	// Merge conflict lists
+	if (face->mFurthestPointDistanceSq > other_face->mFurthestPointDistanceSq)
+	{
+		// This face has a point that's further away, make sure it remains the last one as we add the other points to this faces list
+		face->mConflictList.insert(face->mConflictList.end() - 1, other_face->mConflictList.begin(), other_face->mConflictList.end());
+	}
+	else
+	{
+		// The other face has a point that's furthest away, add that list at the end.
+		face->mConflictList.insert(face->mConflictList.end(), other_face->mConflictList.begin(), other_face->mConflictList.end());
+		face->mFurthestPointDistanceSq = other_face->mFurthestPointDistanceSq;
+	}
+	other_face->mConflictList.clear();
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	DrawWireFace(face, Color::sWhite);
+	DrawState();
+#endif
+}
+
+void ConvexHullBuilder::MergeDegenerateFace(Face *inFace, float inMinAreaSq, Faces &ioAffectedFaces)
+{
+	// Check area of face
+	if (inFace->mNormal.LengthSq() < inMinAreaSq)
+	{
+		// Find longest edge, since this face is a sliver this should keep the face convex
+		float max_length_sq = 0.0f;
+		Edge *longest_edge = nullptr;
+		Edge *e = inFace->mFirstEdge;
+		Vec3 p1 = mPositions[e->mStartIdx];
+		do
+		{
+			Edge *next = e->mNextEdge;
+			Vec3 p2 = mPositions[next->mStartIdx];
+			float length_sq = (p2 - p1).LengthSq();
+			if (length_sq >= max_length_sq)
+			{
+				max_length_sq = length_sq;
+				longest_edge = e;
+			}
+			p1 = p2;	
+			e = next;
+		} while (e != inFace->mFirstEdge);
+
+		// Merge with face on longest edge
+		MergeFaces(longest_edge);
+
+		// Remove any invalid edges
+		RemoveInvalidEdges(inFace, ioAffectedFaces);
+	}
+}
+
+void ConvexHullBuilder::MergeCoplanarOrConcaveFaces(Face *inFace, float inCoplanarToleranceSq, Faces &ioAffectedFaces)
+{
+	bool merged = false;
+
+	Edge *edge = inFace->mFirstEdge;
+	do
+	{
+		// Store next edge since this edge can be removed
+		Edge *next_edge = edge->mNextEdge;
+
+		// Test if centroid of one face is above plane of the other face by inCoplanarToleranceSq.
+		// If so we need to merge other face into inFace.
+		Face *other_face = edge->mNeighbourEdge->mFace;
+		Vec3 delta_centroid = other_face->mCentroid - inFace->mCentroid;
+		float dist_other_face_centroid = inFace->mNormal.Dot(delta_centroid);
+		float signed_dist_other_face_centroid_sq = abs(dist_other_face_centroid) * dist_other_face_centroid;
+		float dist_face_centroid = -other_face->mNormal.Dot(delta_centroid);
+		float signed_dist_face_centroid_sq = abs(dist_face_centroid) * dist_face_centroid;
+		float face_normal_len_sq = inFace->mNormal.LengthSq();
+		float other_face_normal_len_sq = other_face->mNormal.LengthSq();
+		if ((signed_dist_other_face_centroid_sq > -inCoplanarToleranceSq * face_normal_len_sq 
+			|| signed_dist_face_centroid_sq > -inCoplanarToleranceSq * other_face_normal_len_sq)
+			&& inFace->mNormal.Dot(other_face->mNormal) > 0.0f) // Never merge faces that are back to back
+		{
+			MergeFaces(edge);
+			merged = true;
+		}
+
+		edge = next_edge;
+	} while (edge != inFace->mFirstEdge);
+
+	if (merged)
+		RemoveInvalidEdges(inFace, ioAffectedFaces);
+}
+
+void ConvexHullBuilder::sMarkAffected(Face *inFace, Faces &ioAffectedFaces)
+{
+	if (find(ioAffectedFaces.begin(), ioAffectedFaces.end(), inFace) == ioAffectedFaces.end())
+		ioAffectedFaces.push_back(inFace);
+}
+
+void ConvexHullBuilder::RemoveInvalidEdges(Face *inFace, Faces &ioAffectedFaces)
+{
+	// This marks that the plane needs to be recalculated (we delay this until the end of the 
+	// function since we don't use the plane and we want to avoid calculating it multiple times)
+	bool recalculate_plane = false;
+
+	// We keep going through this loop until no more edges were removed
+	bool removed;
+	do
+	{
+		removed = false;
+
+		// Loop over all edges in this face
+		Edge *edge = inFace->mFirstEdge;
+		Face *neighbour_face = edge->mNeighbourEdge->mFace;
+		do
+		{
+			Edge *next_edge = edge->mNextEdge;
+			Face *next_neighbour_face = next_edge->mNeighbourEdge->mFace;
+
+			if (neighbour_face == inFace)
+			{
+				// We only remove 1 edge at a time, check if this edge's next edge is our neighbour.
+				// If this check fails, we will continue to scan along the edge until we find an edge where this is the case.
+				if (edge->mNeighbourEdge == next_edge)
+				{
+					// This edge leads back to the starting point, this means the edge is interior and needs to be removed
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+					DrawWireFace(inFace, Color::sBlue);
+					DrawState();
+#endif
+
+					// Remove edge
+					Edge *prev_edge = edge->GetPreviousEdge();
+					prev_edge->mNextEdge = next_edge->mNextEdge;
+					if (inFace->mFirstEdge == edge || inFace->mFirstEdge == next_edge)
+						inFace->mFirstEdge = prev_edge;
+					delete edge;
+					delete next_edge;
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+					DrawWireFace(inFace, Color::sGreen);
+					DrawState();
+#endif
+
+					// Check if inFace now has only 2 edges left
+					if (RemoveTwoEdgeFace(inFace, ioAffectedFaces))
+						return; // Bail if face no longer exists
+
+					// Restart the loop
+					recalculate_plane = true;
+					removed = true;
+					break;
+				}
+			}
+			else if (neighbour_face == next_neighbour_face)
+			{
+				// There are two edges that connect to the same face, we will remove the second one
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+				DrawWireFace(inFace, Color::sYellow);
+				DrawWireFace(neighbour_face, Color::sRed);
+				DrawState();
+#endif
+
+				// First merge the neighbours edges
+				Edge *neighbour_edge = next_edge->mNeighbourEdge;
+				Edge *next_neighbour_edge = neighbour_edge->mNextEdge;
+				if (neighbour_face->mFirstEdge == next_neighbour_edge)
+					neighbour_face->mFirstEdge = neighbour_edge;
+				neighbour_edge->mNextEdge = next_neighbour_edge->mNextEdge;
+				neighbour_edge->mNeighbourEdge = edge;
+				delete next_neighbour_edge;
+
+				// Then merge my own edges
+				if (inFace->mFirstEdge == next_edge)
+					inFace->mFirstEdge = edge;
+				edge->mNextEdge = next_edge->mNextEdge;
+				edge->mNeighbourEdge = neighbour_edge;
+				delete next_edge;
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+				DrawWireFace(inFace, Color::sYellow);
+				DrawWireFace(neighbour_face, Color::sGreen);
+				DrawState();
+#endif
+
+				// Check if neighbour has only 2 edges left
+				if (!RemoveTwoEdgeFace(neighbour_face, ioAffectedFaces))
+				{
+					// No, we need to recalculate its plane
+					neighbour_face->CalculateNormalAndCentroid(mPositions.data());
+
+					// Mark neighbour face as affected
+					sMarkAffected(neighbour_face, ioAffectedFaces);
+				}
+
+				// Check if inFace now has only 2 edges left
+				if (RemoveTwoEdgeFace(inFace, ioAffectedFaces))
+					return; // Bail if face no longer exists
+
+				// Restart loop
+				recalculate_plane = true;
+				removed = true;
+				break;
+			}
+
+			// This edge is ok, go to the next edge
+			edge = next_edge;
+			neighbour_face = next_neighbour_face;
+
+		} while (edge != inFace->mFirstEdge);
+	} while (removed);
+
+	// Recalculate plane?
+	if (recalculate_plane)
+		inFace->CalculateNormalAndCentroid(mPositions.data());
+}
+
+bool ConvexHullBuilder::RemoveTwoEdgeFace(Face *inFace, Faces &ioAffectedFaces)
+{
+	// Check if this face contains only 2 edges
+	Edge *edge = inFace->mFirstEdge;
+	Edge *next_edge = edge->mNextEdge;
+	JPH_ASSERT(edge != next_edge); // 1 edge faces should not exist
+	if (next_edge->mNextEdge == edge)
+	{
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+		DrawWireFace(inFace, Color::sRed);
+		DrawState();
+#endif
+
+		// Schedule both neighbours for re-checking
+		Edge *neighbour_edge = edge->mNeighbourEdge;
+		Face *neighbour_face = neighbour_edge->mFace;
+		Edge *next_neighbour_edge = next_edge->mNeighbourEdge;
+		Face *next_neighbour_face = next_neighbour_edge->mFace;
+		sMarkAffected(neighbour_face, ioAffectedFaces);
+		sMarkAffected(next_neighbour_face, ioAffectedFaces);
+
+		// Link my neighbours to each other
+		neighbour_edge->mNeighbourEdge = next_neighbour_edge;
+		next_neighbour_edge->mNeighbourEdge = neighbour_edge;
+
+		// Unlink my edges
+		edge->mNeighbourEdge = nullptr;
+		next_edge->mNeighbourEdge = nullptr;
+
+		// Mark this face as removed
+		inFace->mRemoved = true;
+
+		return true;
+	}
+
+	return false;
+}
+	
+#ifdef JPH_ENABLE_ASSERTS
+
+void ConvexHullBuilder::DumpFace(const Face *inFace) const
+{
+	Trace("f:0x%p", inFace);
+
+	const Edge *e = inFace->mFirstEdge;
+	do 
+	{ 
+		Trace("\te:0x%p { i:%d e:0x%p f:0x%p }", e, e->mStartIdx, e->mNeighbourEdge, e->mNeighbourEdge->mFace);
+		e = e->mNextEdge;
+	} while (e != inFace->mFirstEdge);
+}
+
+void ConvexHullBuilder::DumpFaces() const
+{
+	Trace("Dump Faces:");
+
+	for (const Face *f : mFaces)
+		if (!f->mRemoved)
+			DumpFace(f);
+}
+
+void ConvexHullBuilder::ValidateFace(const Face *inFace) const
+{
+	if (inFace->mRemoved)
+	{
+		const Edge *e = inFace->mFirstEdge;
+		if (e != nullptr)
+			do 
+			{ 
+				JPH_ASSERT(e->mNeighbourEdge == nullptr);
+				e = e->mNextEdge;
+			} while (e != inFace->mFirstEdge);
+	}
+	else
+	{
+		int edge_count = 0;
+
+		const Edge *e = inFace->mFirstEdge;
+		do 
+		{ 
+			// Count edge
+			++edge_count;
+
+			// Validate that adjacent faces are all different
+			if (mFaces.size() > 2)
+				for (const Edge *other_edge = e->mNextEdge; other_edge != inFace->mFirstEdge; other_edge = other_edge->mNextEdge)
+					JPH_ASSERT(e->mNeighbourEdge->mFace != other_edge->mNeighbourEdge->mFace);
+
+			// Assert that the face is correct
+			JPH_ASSERT(e->mFace == inFace);
+
+			// Assert that we have a neighbour
+			const Edge *nb_edge = e->mNeighbourEdge;
+			JPH_ASSERT(nb_edge != nullptr);
+			if (nb_edge != nullptr)
+			{
+				// Assert that our neighbours edge points to us
+				JPH_ASSERT(nb_edge->mNeighbourEdge == e);
+
+				// Assert that it belongs to a different face
+				JPH_ASSERT(nb_edge->mFace != inFace);
+
+				// Assert that the next edge of the neighbour points to the same vertex as this edge's vertex
+				JPH_ASSERT(nb_edge->mNextEdge->mStartIdx == e->mStartIdx);
+
+				// Assert that my next edge points to the same vertex as my neighbours vertex
+				JPH_ASSERT(e->mNextEdge->mStartIdx == nb_edge->mStartIdx);
+			}
+			e = e->mNextEdge;
+		} while (e != inFace->mFirstEdge);
+
+		// Assert that we have 3 or more edges
+		JPH_ASSERT(edge_count >= 3);
+	}
+}
+
+void ConvexHullBuilder::ValidateFaces() const
+{
+	for (const Face *f : mFaces)
+		ValidateFace(f);
+}
+
+#endif // JPH_ENABLE_ASSERTS
+
+void ConvexHullBuilder::GetCenterOfMassAndVolume(Vec3 &outCenterOfMass, float &outVolume) const
+{
+	// Fourth point is the average of all face centroids
+	Vec3 v4 = Vec3::sZero();
+	for (const Face *f : mFaces)
+		v4 += f->mCentroid;
+	v4 /= float(mFaces.size());
+
+	// Calculate mass and center of mass of this convex hull by summing all tetrahedrons
+	outVolume = 0.0f;
+	outCenterOfMass = Vec3::sZero();
+	for (const Face *f : mFaces)
+	{
+		// Get the first vertex that we'll use to create a triangle fan
+		Edge *e = f->mFirstEdge;
+		Vec3 v1 = mPositions[e->mStartIdx];
+
+		// Get the second vertex
+		e = e->mNextEdge;
+		Vec3 v2 = mPositions[e->mStartIdx];
+
+		for (e = e->mNextEdge; e != f->mFirstEdge; e = e->mNextEdge)
+		{ 
+			// Fetch the last point of the triangle
+			Vec3 v3 = mPositions[e->mStartIdx];
+
+			// Calculate center of mass and mass of this tetrahedron,
+			// see: https://en.wikipedia.org/wiki/Tetrahedron#Volume
+			float volume_tetrahedron = (v1 - v4).Dot((v2 - v4).Cross(v3 - v4)); // Needs to be divided by 6, postpone this until the end of the loop
+			Vec3 center_of_mass_tetrahedron = v1 + v2 + v3 + v4; // Needs to be divided by 4, postpone this until the end of the loop
+
+			// Accumulate results
+			outVolume += volume_tetrahedron;
+			outCenterOfMass += volume_tetrahedron * center_of_mass_tetrahedron;
+
+			// Update v2 for next triangle
+			v2 = v3;
+		} while (e != f->mFirstEdge);
+	}
+
+	// Calculate center of mass, fall back to average point in case there is no volume (everything is on a plane in this case)
+	if (outVolume > FLT_EPSILON)
+		outCenterOfMass /= 4.0f * outVolume;
+	else
+		outCenterOfMass = v4;
+
+	outVolume /= 6.0f;
+}
+
+void ConvexHullBuilder::DetermineMaxError(Face *&outFaceWithMaxError, float &outMaxError, int &outMaxErrorPositionIdx, float &outCoplanarDistance) const
+{
+	outCoplanarDistance = DetermineCoplanarDistance();
+
+	// This measures the distance from a polygon to the furthest point outside of the hull
+	float max_error = 0.0f;
+	Face *max_error_face = nullptr;
+	int max_error_point = -1;
+
+	for (int i = 0; i < (int)mPositions.size(); ++i)
+	{
+		Vec3 v = mPositions[i];
+
+		// This measures the closest edge from all faces to point v
+		// Note that we take the min of all faces since there may be multiple near coplanar faces so if we were to test this per face
+		// we may find that a point is outside of a polygon and mark it as an error, while it is actually inside a nearly coplanar
+		// polygon.
+		float min_edge_dist = FLT_MAX;
+		Face *min_edge_dist_face = nullptr;
+
+		for (Face *f : mFaces)
+		{
+			// Check if point is on or in front of plane
+			float normal_len = f->mNormal.Length();
+			JPH_ASSERT(normal_len > 0.0f);
+			float plane_dist = f->mNormal.Dot(v - f->mCentroid) / normal_len;
+			if (plane_dist > -outCoplanarDistance)
+			{
+				bool all_inside = true;
+
+				// Test if it is inside the edges of the polygon
+				Edge *edge = f->mFirstEdge;
+				Vec3 p1 = mPositions[edge->GetPreviousEdge()->mStartIdx];
+				do
+				{
+					Vec3 p2 = mPositions[edge->mStartIdx];
+					if ((p2 - p1).Cross(v - p1).Dot(f->mNormal) < 0.0f)
+					{
+						// It is outside
+						all_inside = false;
+
+						// Measure distance to this edge
+						uint32 s;
+						float edge_dist = ClosestPoint::GetClosestPointOnLine(p1 - v, p2 - v, s).Length();
+						if (edge_dist < min_edge_dist)
+						{
+							min_edge_dist = edge_dist;
+							min_edge_dist_face = f;
+						}
+					}
+					p1 = p2;
+					edge = edge->mNextEdge;
+				} while (edge != f->mFirstEdge);
+
+				if (all_inside)
+				{
+					// The point is inside the polygon, reset distance to edge
+					min_edge_dist = 0.0f;
+					min_edge_dist_face = f;
+
+					// If the point is in front of the plane, measure the distance
+					if (plane_dist > max_error)
+					{
+						max_error = plane_dist;
+						max_error_face = f;
+						max_error_point = i;
+					}
+				}
+			}
+		}
+
+		// If the minimum distance to an edge is further than our current max error, we use that as max error
+		if (min_edge_dist_face != nullptr && min_edge_dist > max_error)
+		{
+			max_error = min_edge_dist;
+			max_error_face = min_edge_dist_face;
+			max_error_point = i;
+		}
+	}
+
+	outFaceWithMaxError = max_error_face;
+	outMaxError = max_error;
+	outMaxErrorPositionIdx = max_error_point;
+}
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+
+void ConvexHullBuilder::DrawState(bool inDrawConflictList)
+{
+	// Draw origin
+	DebugRenderer::sInstance->DrawMarker(cDrawScale * mOffset, Color::sRed, 0.2f);
+
+	int face_idx = 0;
+
+	// Draw faces
+	for (const Face *f : mFaces)
+		if (!f->mRemoved)
+		{
+			Color iteration_color = Color::sGetDistinctColor(f->mIteration);
+			Color face_color = Color::sGetDistinctColor(face_idx++);
+
+			// First point
+			const Edge *e = f->mFirstEdge;
+			Vec3 p1 = cDrawScale * (mPositions[e->mStartIdx] + mOffset);
+
+			// Second point
+			e = e->mNextEdge;
+			Vec3 p2 = cDrawScale * (mPositions[e->mStartIdx] + mOffset);
+
+			// First line
+			DebugRenderer::sInstance->DrawLine(p1, p2, Color::sGrey);
+
+			do
+			{
+				// Third point
+				e = e->mNextEdge;
+				Vec3 p3 = cDrawScale * (mPositions[e->mStartIdx] + mOffset);
+
+				DebugRenderer::sInstance->DrawTriangle(p1, p2, p3, iteration_color);
+
+				DebugRenderer::sInstance->DrawLine(p2, p3, Color::sGrey);
+
+				p2 = p3;
+			}
+			while (e != f->mFirstEdge);
+
+			// Draw normal
+			Vec3 centroid = cDrawScale * (f->mCentroid + mOffset);
+			DebugRenderer::sInstance->DrawArrow(centroid, centroid + f->mNormal.Normalized(), face_color, 0.01f);
+
+			// Draw conflict list
+			if (inDrawConflictList)
+				for (int idx : f->mConflictList)
+					DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + mPositions[idx]), face_color, 0.05f);
+		}
+
+	// Offset to the right
+	mOffset += mDelta;
+}
+
+void ConvexHullBuilder::DrawWireFace(const Face *inFace, ColorArg inColor) const
+{
+	const Edge *e = inFace->mFirstEdge;
+	Vec3 prev = cDrawScale * (mPositions[e->mStartIdx] + mOffset);
+	do
+	{ 
+		const Edge *next = e->mNextEdge;
+		Vec3 cur = cDrawScale * (mPositions[next->mStartIdx] + mOffset);
+		DebugRenderer::sInstance->DrawArrow(prev, cur, inColor, 0.01f);
+		DebugRenderer::sInstance->DrawText3D(prev, ConvertToString(e->mStartIdx), inColor);
+		e = next;
+		prev = cur;
+	} while (e != inFace->mFirstEdge);
+}
+
+void ConvexHullBuilder::DrawEdge(const Edge *inEdge, ColorArg inColor) const
+{
+	Vec3 p1 = cDrawScale * (mPositions[inEdge->mStartIdx] + mOffset);
+	Vec3 p2 = cDrawScale * (mPositions[inEdge->mNextEdge->mStartIdx] + mOffset);
+	DebugRenderer::sInstance->DrawArrow(p1, p2, inColor, 0.01f);
+}
+
+#endif // JPH_CONVEX_BUILDER_DEBUG
+
+#ifdef JPH_CONVEX_BUILDER_DUMP_SHAPE
+
+void ConvexHullBuilder::DumpShape() const
+{
+	static atomic<int> sShapeNo = 1;
+	int shape_no = sShapeNo++;
+
+	ofstream f;
+	f.open(StringFormat("dumped_shape%d.cpp", shape_no).c_str(), ofstream::out | ofstream::trunc);
+	if (!f.is_open()) 
+		return;
+
+	f << "{\n";
+	for (Vec3 v : mPositions)
+		f << StringFormat("\tVec3(%.9gf, %.9gf, %.9gf),\n", v.GetX(), v.GetY(), v.GetZ());
+	f << "},\n";
+}
+
+#endif // JPH_CONVEX_BUILDER_DUMP_SHAPE
+
+} // JPH

+ 243 - 0
Jolt/Geometry/ConvexHullBuilder.h

@@ -0,0 +1,243 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+//#define JPH_CONVEX_BUILDER_DEBUG
+//#define JPH_CONVEX_BUILDER_DUMP_SHAPE
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	#include <Core/Color.h>
+#endif
+
+#include <Core/StaticArray.h>
+#include <Core/NonCopyable.h>
+
+namespace JPH {
+
+/// A convex hull builder that tries to create hulls as accurately as possible. Used for offline processing.
+class ConvexHullBuilder : public NonCopyable
+{
+public:
+	// Forward declare
+	class Face;
+
+	/// Class that holds the information of an edge
+	class Edge : public NonCopyable
+	{
+	public:
+		/// Constructor
+						Edge(Face *inFace, int inStartIdx)	: mFace(inFace), mStartIdx(inStartIdx) { }
+
+		/// Get the previous edge
+		inline Edge *	GetPreviousEdge()
+		{
+			Edge *prev_edge = this;
+			while (prev_edge->mNextEdge != this)
+				prev_edge = prev_edge->mNextEdge;
+			return prev_edge;
+		}
+
+		Face *			mFace;								///< Face that this edge belongs to
+		Edge *			mNextEdge = nullptr;				///< Next edge of this face
+		Edge *			mNeighbourEdge = nullptr;			///< Edge that this edge is connected to
+		int				mStartIdx;							///< Vertex index in mPositions that indicates the start vertex of this edge
+	};
+
+	using ConflictList = vector<int>;
+
+	/// Class that holds the information of one face
+	class Face : public NonCopyable
+	{
+	public:
+		/// Destructor
+						~Face();
+
+		/// Initialize a face with three indices
+		void			Initialize(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions);
+
+		/// Calculates the centroid and normal for this face
+		void			CalculateNormalAndCentroid(const Vec3 *inPositions);
+
+		/// Check if face inFace is facing inPosition
+		inline bool		IsFacing(Vec3Arg inPosition) const
+		{
+			JPH_ASSERT(!mRemoved);
+			return mNormal.Dot(inPosition - mCentroid) > 0.0f;
+		}
+
+		Vec3			mNormal;							///< Normal of this face, length is 2 times area of face
+		Vec3			mCentroid;							///< Center of the face
+		ConflictList	mConflictList;						///< Positions associated with this edge (that are closest to this edge). The last position in the list is the point that is furthest away from the face.
+		Edge *			mFirstEdge = nullptr;				///< First edge of this face
+		float			mFurthestPointDistanceSq = 0.0f;	///< Squared distance of furtest point from the conflict list to the face
+		bool			mRemoved = false;					///< Flag that indicates that face has been removed (face will be freed later)
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+		int				mIteration;							///< Iteration that this face was created
+#endif
+	};
+
+	// Typedefs
+	using Positions = vector<Vec3>;
+	using Faces = vector<Face *>;
+
+	/// Constructor
+						ConvexHullBuilder(const Positions &inPositions);
+
+	/// Destructor
+						~ConvexHullBuilder()				{ FreeFaces(); }
+
+	/// Result enum that indicates how the hull got created
+	enum class EResult
+	{
+		Success,											///< Hull building finished successfully
+		MaxVerticesReached,									///< Hull building finished successfully, but the desired accuracy was not reached because the max vertices limit was reached
+		TooFewPoints,										///< Too few points to create a hull
+		Degenerate,											///< Degenerate hull detected
+	};
+
+	/// Takes all positions as provided by the constructor and use them to build a hull
+	/// Any points that are closer to the hull than inTolerance will be discarded
+	/// @param inMaxVertices Max vertices to allow in the hull. Specify INT_MAX if there is no limit.
+	/// @param inTolerance Max distance that a point is allowed to be outside of the hull
+	/// @param outError Error message when building fails
+	/// @return Status code that reports if the hull was created or not
+	EResult				Initialize(int inMaxVertices, float inTolerance, string &outError);
+
+	/// Returns the amount of vertices that are currently used by the hull
+	int					GetNumVerticesUsed() const;
+
+	/// Returns true if the hull contains a polygon with inIndices (counter clockwise indices in mPositions)
+	bool				ContainsFace(const vector<int> &inIndices);
+
+	/// Calculate the center of mass and the volume of the current convex hull
+	void				GetCenterOfMassAndVolume(Vec3 &outCenterOfMass, float &outVolume) const;
+
+	/// Determines the point that is furthest outside of the hull and reports how far it is outside of the hull (which indicates a failure during hull building)
+	/// @param outFaceWithMaxError The face that caused the error
+	/// @param outMaxError The maximum distance of a point to the hull
+	/// @param outMaxErrorPositionIdx The index of the point that had this distance
+	/// @param outCoplanarDistance Points that are less than this distance from the hull are considered on the hull. This should be used as a lowerbound for the allowed error.
+	void				DetermineMaxError(Face *&outFaceWithMaxError, float &outMaxError, int &outMaxErrorPositionIdx, float &outCoplanarDistance) const;
+
+	/// Access to the created faces. Memory is owned by the convex hull builder.
+	const Faces &		GetFaces() const					{ return mFaces; }
+
+private:
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	/// Factor to scale convex hull when debug drawing the construction process
+	static constexpr float cDrawScale = 10.0f;
+#endif
+
+	/// Class that holds an edge including start and end index
+	class FullEdge
+	{
+	public:
+		Edge *			mNeighbourEdge;						///< Edge that this edge is connected to
+		int				mStartIdx;							///< Vertex index in mPositions that indicates the start vertex of this edge
+		int				mEndIdx;							///< Vertex index in mPosition that indicats the end vertex of this edge
+	};
+
+	// Private typedefs
+	using FullEdges = vector<FullEdge>;
+
+	// Determine a suitable tolerance for detecting that points are coplanar
+	float				DetermineCoplanarDistance() const;
+
+	/// Assigns a position to one of the supplied faces based on which face is closest.
+	/// @param inPositionIdx Index of the position to add
+	/// @param inFaces List of faces to consider
+	void				AssignPointToFace(int inPositionIdx, const Faces &inFaces) const;
+
+	/// Add a new point to the convex hull
+	void				AddPoint(Face *inFacingFace, int inIdx, float inToleranceSq, Faces &outNewFaces);
+
+	/// Remove all faces that have been marked 'removed' from mFaces list
+	void				GarbageCollectFaces();
+
+	/// Create a new face
+	Face *				CreateFace();
+
+	/// Create a new triangle
+	Face *				CreateTriangle(int inIdx1, int inIdx2, int inIdx3);
+
+	/// Delete a face (checking that it is not connected to any other faces)
+	void				FreeFace(Face *inFace);
+
+	/// Release all faces and edges
+	void				FreeFaces();
+
+	/// Link face edge to other face edge
+	void				LinkFace(Edge *inEdge1, Edge *inEdge2);
+
+	/// Unlink this face from all of its neighbours
+	void				UnlinkFace(Face *inFace);
+
+	/// Given one face that faces inVertex, find the edges of the faces that are not facing inVertex.
+	/// Will flag all those faces for removal.
+	void				FindEdge(Face *inFacingFace, Vec3Arg inVertex, FullEdges &outEdges);
+
+	/// Merges the two faces that share inEdge into the face inEdge->mFace
+	void				MergeFaces(Edge *inEdge);
+
+	/// Merges inFace with a neighbour if it is degenerate (a sliver)
+	void				MergeDegenerateFace(Face *inFace, float inMinAreaSq, Faces &ioAffectedFaces);
+
+	/// Merges any coplanar as well as neighbours that form a non-convex edge into inFace. 
+	/// Faces are considered coplanar if the distance^2 of the other face's centroid is smaller than inToleranceSq.
+	void				MergeCoplanarOrConcaveFaces(Face *inFace, float inToleranceSq, Faces &ioAffectedFaces);
+
+	/// Mark face as affected if it is not already in the list
+	static void			sMarkAffected(Face *inFace, Faces &ioAffectedFaces);
+
+	/// Removes all invalid edges.
+	/// 1. Merges inFace with faces that share two edges with it since this means inFace or the other face cannot be convex or the edge is colinear.
+	/// 2. Removes edges that are interior to inFace (that have inFace on both sides)
+	/// Any faces that need to be checked for validity will be added to ioAffectedFaces.
+	void				RemoveInvalidEdges(Face *inFace, Faces &ioAffectedFaces);
+
+	/// Removes inFace if it consists of only 2 edges, linking its neighbouring faces together
+	/// Any faces that need to be checked for validity will be added to ioAffectedFaces.
+	/// @return True if face was removed.
+	bool				RemoveTwoEdgeFace(Face *inFace, Faces &ioAffectedFaces);
+
+#ifdef JPH_ENABLE_ASSERTS
+	/// Dumps the text representation of a face to the TTY
+	void				DumpFace(const Face *inFace) const;
+
+	/// Dumps the text representation of all faces to the TTY
+	void				DumpFaces() const;
+
+	/// Check consistency of 1 face
+	void				ValidateFace(const Face *inFace) const;
+
+	/// Check consistency of all faces
+	void				ValidateFaces() const;
+#endif
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	/// Draw state of algorithm
+	void				DrawState(bool inDrawConflictList = false);
+
+	/// Draw a face for debugging purposes
+	void				DrawWireFace(const Face *inFace, ColorArg inColor) const;
+
+	/// Draw an edge for debugging purposes
+	void				DrawEdge(const Edge *inEdge, ColorArg inColor) const;
+#endif
+
+#ifdef JPH_CONVEX_BUILDER_DUMP_SHAPE
+	void				DumpShape() const;
+#endif
+
+	const Positions &	mPositions;							///< List of positions (some of them are part of the hull)
+	Faces 				mFaces;								///< List of faces that are part of the hull (if !mRemoved)
+
+#ifdef JPH_CONVEX_BUILDER_DEBUG
+	int					mIteration;							///< Number of iterations we've had so far (for debug purposes)	
+	Vec3				mOffset;							///< Offset to use for state drawing
+	Vec3				mDelta;								///< Delta offset between next states
+#endif
+};
+
+} // JPH

+ 335 - 0
Jolt/Geometry/ConvexHullBuilder2D.cpp

@@ -0,0 +1,335 @@
+// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt.h>
+
+#include <Geometry/ConvexHullBuilder2D.h>
+
+#ifdef JPH_CONVEX_BUILDER_2D_DEBUG
+	#include <Renderer/DebugRenderer.h>
+#endif
+
+namespace JPH {
+
+void ConvexHullBuilder2D::Edge::CalculateNormalAndCenter(const Vec3 *inPositions)
+{
+	Vec3 p1 = inPositions[mStartIdx];
+	Vec3 p2 = inPositions[mNextEdge->mStartIdx];
+
+	// Center of edge
+	mCenter = 0.5f * (p1 + p2);
+			
+	// Create outward pointing normal. 
+	// We have two choices for the normal (which satisfies normal . edge = 0):
+	// normal1 = (-edge.y, edge.x, 0) 
+	// normal2 = (edge.y, -edge.x, 0)
+	// We want (normal x edge).z > 0 so that the normal points out of the polygon. Only normal2 satisfies this condition.
+	Vec3 edge = p2 - p1;
+	mNormal = Vec3(edge.GetY(), -edge.GetX(), 0);
+}
+
+ConvexHullBuilder2D::ConvexHullBuilder2D(const Positions &inPositions) :
+	mPositions(inPositions)
+{
+#ifdef JPH_CONVEX_BUILDER_2D_DEBUG
+	// Center the drawing of the first hull around the origin and calculate the delta offset between states
+	mOffset = Vec3::sZero();
+	if (mPositions.empty())
+	{
+		// No hull will be generated
+		mDelta = Vec3::sZero();
+	}
+	else
+	{
+		Vec3 maxv = Vec3::sReplicate(-FLT_MAX), minv = Vec3::sReplicate(FLT_MAX);
+		for (Vec3 v : mPositions)
+		{
+			minv = Vec3::sMin(minv, v);
+			maxv = Vec3::sMax(maxv, v);
+			mOffset -= v;
+		}
+		mOffset /= float(mPositions.size());
+		mDelta = Vec3((maxv - minv).GetX() + 0.5f, 0, 0);
+		mOffset += mDelta; // Don't start at origin, we're already drawing the final hull there
+	}
+#endif
+}
+
+ConvexHullBuilder2D::~ConvexHullBuilder2D()
+{
+	FreeEdges();
+}
+
+void ConvexHullBuilder2D::FreeEdges()
+{
+	if (mFirstEdge == nullptr)
+		return;
+
+	Edge *edge = mFirstEdge;
+	do
+	{
+		Edge *next = edge->mNextEdge;
+		delete edge;
+		edge = next;
+	} while (edge != mFirstEdge);
+
+	mFirstEdge = nullptr;
+	mNumEdges = 0;
+}
+
+#ifdef JPH_ENABLE_ASSERTS
+
+void ConvexHullBuilder2D::ValidateEdges() const
+{
+	if (mFirstEdge == nullptr)
+	{
+		JPH_ASSERT(mNumEdges == 0);
+		return;
+	}
+
+	int count = 0;
+
+	Edge *edge = mFirstEdge;
+	do
+	{
+		// Validate connectivity
+		JPH_ASSERT(edge->mNextEdge->mPrevEdge == edge);
+		JPH_ASSERT(edge->mPrevEdge->mNextEdge == edge);
+
+		++count;
+		edge = edge->mNextEdge;
+	} while (edge != mFirstEdge);	
+
+	// Validate that count matches
+	JPH_ASSERT(count == mNumEdges);
+}
+
+#endif // JPH_ENABLE_ASSERTS
+
+void ConvexHullBuilder2D::AssignPointToEdge(int inPositionIdx, const vector<Edge *> &inEdges) const
+{
+	Vec3 point = mPositions[inPositionIdx];
+
+	Edge *best_edge = nullptr;
+	float best_dist_sq = 0.0f;
+
+	// Test against all edges
+	for (Edge *edge : inEdges)
+	{
+		// Determine distance to edge
+		float dot = edge->mNormal.Dot(point - edge->mCenter);
+		if (dot > 0.0f)
+		{
+			float dist_sq = dot * dot / edge->mNormal.LengthSq();
+			if (dist_sq > best_dist_sq)
+			{
+				best_edge = edge;
+				best_dist_sq = dist_sq;
+			}
+		}
+	}
+
+	// If this point is in front of the edge, add it to the conflict list
+	if (best_edge != nullptr)
+	{
+		if (best_dist_sq > best_edge->mFurthestPointDistanceSq)
+		{
+			// This point is futher away than any others, update the distance and add point as last point
+			best_edge->mFurthestPointDistanceSq = best_dist_sq;
+			best_edge->mConflictList.push_back(inPositionIdx);
+		}
+		else
+		{
+			// Not the furthest point, add it as the before last point
+			best_edge->mConflictList.push_back(best_edge->mConflictList.back());
+			best_edge->mConflictList[best_edge->mConflictList.size() - 2] = inPositionIdx;
+		}
+	}
+}
+
+ConvexHullBuilder2D::EResult ConvexHullBuilder2D::Initialize(int inIdx1, int inIdx2, int inIdx3, int inMaxVertices, float inTolerance, Edges &outEdges)
+{
+	// Clear any leftovers
+	FreeEdges();
+	outEdges.clear();
+
+	// Reset flag
+	EResult result = EResult::Success;
+
+	// Determine a suitable tolerance for detecting that points are colinear
+	// Formula as per: Implementing Quickhull - Dirk Gregorius.
+	Vec3 vmax = Vec3::sZero();
+	for (Vec3 v : mPositions)
+		vmax = Vec3::sMax(vmax, v.Abs());
+	float colinear_tolerance_sq = Square(2.0f * FLT_EPSILON * (vmax.GetX() + vmax.GetY()));
+
+	// Increase desired tolerance if accuracy doesn't allow it
+	float tolerance_sq = max(colinear_tolerance_sq, Square(inTolerance));
+
+	// Start with the initial indices in counter clockwise order
+	float z = (mPositions[inIdx2] - mPositions[inIdx1]).Cross(mPositions[inIdx3] - mPositions[inIdx1]).GetZ();
+	if (z < 0.0f)
+		swap(inIdx1, inIdx2);
+
+	// Create and link edges
+	Edge *e1 = new Edge(inIdx1);
+	Edge *e2 = new Edge(inIdx2);
+	Edge *e3 = new Edge(inIdx3);
+	e1->mNextEdge = e2;
+	e1->mPrevEdge = e3;
+	e2->mNextEdge = e3;
+	e2->mPrevEdge = e1;
+	e3->mNextEdge = e1;
+	e3->mPrevEdge = e2;
+	mFirstEdge = e1;
+	mNumEdges = 3;
+
+	// Build the initial conflict lists
+	vector<Edge *> edges { e1, e2, e3 };
+	for (Edge *edge : edges)
+		edge->CalculateNormalAndCenter(mPositions.data());
+	for (int idx = 0; idx < (int)mPositions.size(); ++idx)
+		if (idx != inIdx1 && idx != inIdx2 && idx != inIdx3)
+			AssignPointToEdge(idx, edges);
+
+	JPH_IF_ENABLE_ASSERTS(ValidateEdges();)
+#ifdef JPH_CONVEX_BUILDER_2D_DEBUG
+	DrawState();
+#endif
+
+	// Add the remaining points to the hull
+	for (;;)
+	{
+		// Check if we've reached the max amount of vertices that are allowed
+		if (mNumEdges >= inMaxVertices)
+		{
+			result = EResult::MaxVerticesReached;
+			break;
+		}
+
+		// Find the edge with the furthest point on it
+		Edge *edge_with_furthest_point = nullptr;
+		float furthest_dist_sq = 0.0f;
+		Edge *edge = mFirstEdge;
+		do
+		{
+			if (edge->mFurthestPointDistanceSq > furthest_dist_sq)
+			{
+				furthest_dist_sq = edge->mFurthestPointDistanceSq;
+				edge_with_furthest_point = edge;
+			}
+			edge = edge->mNextEdge;
+		} while (edge != mFirstEdge);
+
+		// If there is none closer than our tolerance value, we're done
+		if (edge_with_furthest_point == nullptr || furthest_dist_sq < tolerance_sq)
+			break;
+
+		// Take the furthest point
+		int furthest_point_idx = edge_with_furthest_point->mConflictList.back();
+		edge_with_furthest_point->mConflictList.pop_back();
+		Vec3 furthest_point = mPositions[furthest_point_idx];
+
+		// Find the horizon of edges that need to be removed
+		Edge *first_edge = edge_with_furthest_point;
+		do
+		{
+			Edge *prev = first_edge->mPrevEdge;
+			if (!prev->IsFacing(furthest_point))
+				break;
+			first_edge = prev;
+		} while (first_edge != edge_with_furthest_point);
+
+		Edge *last_edge = edge_with_furthest_point;
+		do
+		{
+			Edge *next = last_edge->mNextEdge;
+			if (!next->IsFacing(furthest_point))
+				break;
+			last_edge = next;
+		} while (last_edge != edge_with_furthest_point);
+
+		// Create new edges
+		e1 = new Edge(first_edge->mStartIdx);
+		e2 = new Edge(furthest_point_idx);
+		e1->mNextEdge = e2;
+		e1->mPrevEdge = first_edge->mPrevEdge;
+		e2->mPrevEdge = e1;
+		e2->mNextEdge = last_edge->mNextEdge;
+		e1->mPrevEdge->mNextEdge = e1;
+		e2->mNextEdge->mPrevEdge = e2;
+		mFirstEdge = e1; // We could delete mFirstEdge so just update it to the newly created edge
+		mNumEdges += 2;
+
+		// Calculate normals
+		vector<Edge *> new_edges { e1, e2 };
+		for (Edge *new_edge : new_edges)
+			new_edge->CalculateNormalAndCenter(mPositions.data());
+
+		// Delete the old edges
+		for (;;)
+		{
+			Edge *next = first_edge->mNextEdge;
+
+			// Redistribute points in conflict list
+			for (int idx : first_edge->mConflictList)
+				AssignPointToEdge(idx, new_edges);
+
+			// Delete the old edge
+			delete first_edge;
+			--mNumEdges;
+
+			if (first_edge == last_edge)
+				break;
+			first_edge = next;
+		}
+
+		JPH_IF_ENABLE_ASSERTS(ValidateEdges();)
+	#ifdef JPH_CONVEX_BUILDER_2D_DEBUG
+		DrawState();
+	#endif
+	}
+
+	// Convert the edge list to a list of indices
+	outEdges.reserve(mNumEdges);
+	Edge *edge = mFirstEdge;
+	do
+	{
+		outEdges.push_back(edge->mStartIdx);
+		edge = edge->mNextEdge;
+	} while (edge != mFirstEdge);
+
+	return result;
+}
+
+#ifdef JPH_CONVEX_BUILDER_2D_DEBUG
+
+void ConvexHullBuilder2D::DrawState()
+{
+	int color_idx = 0;
+
+	const Edge *edge = mFirstEdge;
+	do
+	{
+		const Edge *next = edge->mNextEdge;
+
+		// Get unique color per edge
+		Color color = Color::sGetDistinctColor(color_idx++);
+
+		// Draw edge and normal
+		DebugRenderer::sInstance->DrawArrow(cDrawScale * (mPositions[edge->mStartIdx] + mOffset), cDrawScale * (mPositions[next->mStartIdx] + mOffset), color, 0.1f);
+		DebugRenderer::sInstance->DrawArrow(cDrawScale * (edge->mCenter + mOffset), cDrawScale * (edge->mCenter + mOffset) + edge->mNormal.NormalizedOr(Vec3::sZero()), Color::sGreen, 0.1f);
+
+		// Draw points that belong to this edge in the same color
+		for (int idx : edge->mConflictList)
+			DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + mPositions[idx]), color, 0.05f);
+
+		edge = next;
+	} while (edge != mFirstEdge);
+
+	mOffset += mDelta;
+}
+
+#endif
+
+} // JPH

Някои файлове не бяха показани, защото твърде много файлове са промени